Skip to content

Commit 5cfb3a2

Browse files
authoredOct 28, 2022
Only call return() for an abrupt completion in user code (#51297)
1 parent a7a9d15 commit 5cfb3a2

13 files changed

+528
-266
lines changed
 

‎src/compiler/transformers/es2018.ts

+40-10
Original file line numberDiff line numberDiff line change
@@ -643,12 +643,26 @@ namespace ts {
643643
return node;
644644
}
645645

646-
function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression) {
647-
const binding = createForOfBindingStatement(factory, node.initializer, boundValue);
646+
function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression, nonUserCode: Identifier) {
647+
const value = factory.createTempVariable(hoistVariableDeclaration);
648+
const iteratorValueExpression = factory.createAssignment(value, boundValue);
649+
const iteratorValueStatement = factory.createExpressionStatement(iteratorValueExpression);
650+
setSourceMapRange(iteratorValueStatement, node.expression);
651+
652+
const exitNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createFalse());
653+
const exitNonUserCodeStatement = factory.createExpressionStatement(exitNonUserCodeExpression);
654+
setSourceMapRange(exitNonUserCodeStatement, node.expression);
655+
656+
const enterNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createTrue());
657+
const enterNonUserCodeStatement = factory.createExpressionStatement(enterNonUserCodeExpression);
658+
setSourceMapRange(exitNonUserCodeStatement, node.expression);
659+
660+
const statements: Statement[] = [];
661+
const binding = createForOfBindingStatement(factory, node.initializer, value);
662+
statements.push(visitNode(binding, visitor, isStatement));
648663

649664
let bodyLocation: TextRange | undefined;
650665
let statementsLocation: TextRange | undefined;
651-
const statements: Statement[] = [visitNode(binding, visitor, isStatement)];
652666
const statement = visitIterationBody(node.statement, visitor, context);
653667
if (isBlock(statement)) {
654668
addRange(statements, statement.statements);
@@ -659,7 +673,7 @@ namespace ts {
659673
statements.push(statement);
660674
}
661675

662-
return setEmitFlags(
676+
const body = setEmitFlags(
663677
setTextRange(
664678
factory.createBlock(
665679
setTextRange(factory.createNodeArray(statements), statementsLocation),
@@ -669,6 +683,18 @@ namespace ts {
669683
),
670684
EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps
671685
);
686+
687+
return factory.createBlock([
688+
iteratorValueStatement,
689+
exitNonUserCodeStatement,
690+
factory.createTryStatement(
691+
body,
692+
/*catchClause*/ undefined,
693+
factory.createBlock([
694+
enterNonUserCodeStatement
695+
])
696+
)
697+
]);
672698
}
673699

674700
function createDownlevelAwait(expression: Expression) {
@@ -681,6 +707,8 @@ namespace ts {
681707
const expression = visitNode(node.expression, visitor, isExpression);
682708
const iterator = isIdentifier(expression) ? factory.getGeneratedNameForNode(expression) : factory.createTempVariable(/*recordTempVariable*/ undefined);
683709
const result = isIdentifier(expression) ? factory.getGeneratedNameForNode(iterator) : factory.createTempVariable(/*recordTempVariable*/ undefined);
710+
const nonUserCode = factory.createTempVariable(/*recordTempVariable*/ undefined);
711+
const done = factory.createTempVariable(hoistVariableDeclaration);
684712
const errorRecord = factory.createUniqueName("e");
685713
const catchVariable = factory.getGeneratedNameForNode(errorRecord);
686714
const returnMethod = factory.createTempVariable(/*recordTempVariable*/ undefined);
@@ -704,19 +732,21 @@ namespace ts {
704732
/*initializer*/ setEmitFlags(
705733
setTextRange(
706734
factory.createVariableDeclarationList([
735+
factory.createVariableDeclaration(nonUserCode, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createTrue()),
707736
setTextRange(factory.createVariableDeclaration(iterator, /*exclamationToken*/ undefined, /*type*/ undefined, initializer), node.expression),
708737
factory.createVariableDeclaration(result)
709738
]),
710739
node.expression
711740
),
712741
EmitFlags.NoHoisting
713742
),
714-
/*condition*/ factory.createComma(
743+
/*condition*/ factory.inlineExpressions([
715744
factory.createAssignment(result, createDownlevelAwait(callNext)),
716-
factory.createLogicalNot(getDone)
717-
),
745+
factory.createAssignment(done, getDone),
746+
factory.createLogicalNot(done)
747+
]),
718748
/*incrementor*/ undefined,
719-
/*statement*/ convertForOfStatementHead(node, getValue)
749+
/*statement*/ convertForOfStatementHead(node, getValue, nonUserCode)
720750
),
721751
/*location*/ node
722752
),
@@ -754,8 +784,8 @@ namespace ts {
754784
factory.createIfStatement(
755785
factory.createLogicalAnd(
756786
factory.createLogicalAnd(
757-
result,
758-
factory.createLogicalNot(getDone)
787+
factory.createLogicalNot(nonUserCode),
788+
factory.createLogicalNot(done),
759789
),
760790
factory.createAssignment(
761791
returnMethod,

‎src/testRunner/unittests/evaluation/forAwaitOf.ts

+27
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,31 @@ describe("unittests:: evaluation:: forAwaitOfEvaluation", () => {
102102
assert.instanceOf(result.output[1], Promise);
103103
assert.instanceOf(result.output[2], Promise);
104104
});
105+
106+
it("don't call return when non-user code throws (es2015)", async () => {
107+
const result = evaluator.evaluateTypeScript(`
108+
let returnCalled = false;
109+
async function f() {
110+
let i = 0;
111+
const iterator = {
112+
[Symbol.asyncIterator](): AsyncIterableIterator<any> { return this; },
113+
async next() {
114+
i++;
115+
if (i < 2) return { value: undefined, done: false };
116+
throw new Error();
117+
},
118+
async return() {
119+
returnCalled = true;
120+
}
121+
};
122+
for await (const item of iterator) {
123+
}
124+
}
125+
export async function main() {
126+
try { await f(); } catch { }
127+
return returnCalled;
128+
}
129+
`, { target: ts.ScriptTarget.ES2015 });
130+
assert.isFalse(await result.main());
131+
});
105132
});

‎tests/baselines/reference/emitter.forAwait(target=es2015).js

+79-30
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,25 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
6868
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
6969
};
7070
function f1() {
71-
var e_1, _a;
71+
var _a, e_1, _b, _c;
7272
return __awaiter(this, void 0, void 0, function* () {
7373
let y;
7474
try {
75-
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
76-
const x = y_1_1.value;
75+
for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), _a = y_1_1.done, !_a;) {
76+
_c = y_1_1.value;
77+
_d = false;
78+
try {
79+
const x = _c;
80+
}
81+
finally {
82+
_d = true;
83+
}
7784
}
7885
}
7986
catch (e_1_1) { e_1 = { error: e_1_1 }; }
8087
finally {
8188
try {
82-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield _a.call(y_1);
89+
if (!_d && !_a && (_b = y_1.return)) yield _b.call(y_1);
8390
}
8491
finally { if (e_1) throw e_1.error; }
8592
}
@@ -103,18 +110,25 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
103110
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
104111
};
105112
function f2() {
106-
var e_1, _a;
113+
var _a, e_1, _b, _c;
107114
return __awaiter(this, void 0, void 0, function* () {
108115
let x, y;
109116
try {
110-
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
111-
x = y_1_1.value;
117+
for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), _a = y_1_1.done, !_a;) {
118+
_c = y_1_1.value;
119+
_d = false;
120+
try {
121+
x = _c;
122+
}
123+
finally {
124+
_d = true;
125+
}
112126
}
113127
}
114128
catch (e_1_1) { e_1 = { error: e_1_1 }; }
115129
finally {
116130
try {
117-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield _a.call(y_1);
131+
if (!_d && !_a && (_b = y_1.return)) yield _b.call(y_1);
118132
}
119133
finally { if (e_1) throw e_1.error; }
120134
}
@@ -142,17 +156,24 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
142156
};
143157
function f3() {
144158
return __asyncGenerator(this, arguments, function* f3_1() {
145-
var e_1, _a;
159+
var _a, e_1, _b, _c;
146160
let y;
147161
try {
148-
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
149-
const x = y_1_1.value;
162+
for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), _a = y_1_1.done, !_a;) {
163+
_c = y_1_1.value;
164+
_d = false;
165+
try {
166+
const x = _c;
167+
}
168+
finally {
169+
_d = true;
170+
}
150171
}
151172
}
152173
catch (e_1_1) { e_1 = { error: e_1_1 }; }
153174
finally {
154175
try {
155-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1));
176+
if (!_d && !_a && (_b = y_1.return)) yield __await(_b.call(y_1));
156177
}
157178
finally { if (e_1) throw e_1.error; }
158179
}
@@ -180,17 +201,24 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
180201
};
181202
function f4() {
182203
return __asyncGenerator(this, arguments, function* f4_1() {
183-
var e_1, _a;
204+
var _a, e_1, _b, _c;
184205
let x, y;
185206
try {
186-
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
187-
x = y_1_1.value;
207+
for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), _a = y_1_1.done, !_a;) {
208+
_c = y_1_1.value;
209+
_d = false;
210+
try {
211+
x = _c;
212+
}
213+
finally {
214+
_d = true;
215+
}
188216
}
189217
}
190218
catch (e_1_1) { e_1 = { error: e_1_1 }; }
191219
finally {
192220
try {
193-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1));
221+
if (!_d && !_a && (_b = y_1.return)) yield __await(_b.call(y_1));
194222
}
195223
finally { if (e_1) throw e_1.error; }
196224
}
@@ -215,19 +243,26 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
215243
};
216244
// https://github.com/Microsoft/TypeScript/issues/21363
217245
function f5() {
218-
var e_1, _a;
246+
var _a, e_1, _b, _c;
219247
return __awaiter(this, void 0, void 0, function* () {
220248
let y;
221249
try {
222-
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
223-
const x = y_1_1.value;
224-
continue outer;
250+
outer: for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), _a = y_1_1.done, !_a;) {
251+
_c = y_1_1.value;
252+
_d = false;
253+
try {
254+
const x = _c;
255+
continue outer;
256+
}
257+
finally {
258+
_d = true;
259+
}
225260
}
226261
}
227262
catch (e_1_1) { e_1 = { error: e_1_1 }; }
228263
finally {
229264
try {
230-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield _a.call(y_1);
265+
if (!_d && !_a && (_b = y_1.return)) yield _b.call(y_1);
231266
}
232267
finally { if (e_1) throw e_1.error; }
233268
}
@@ -256,18 +291,25 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
256291
// https://github.com/Microsoft/TypeScript/issues/21363
257292
function f6() {
258293
return __asyncGenerator(this, arguments, function* f6_1() {
259-
var e_1, _a;
294+
var _a, e_1, _b, _c;
260295
let y;
261296
try {
262-
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
263-
const x = y_1_1.value;
264-
continue outer;
297+
outer: for (var _d = true, y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), _a = y_1_1.done, !_a;) {
298+
_c = y_1_1.value;
299+
_d = false;
300+
try {
301+
const x = _c;
302+
continue outer;
303+
}
304+
finally {
305+
_d = true;
306+
}
265307
}
266308
}
267309
catch (e_1_1) { e_1 = { error: e_1_1 }; }
268310
finally {
269311
try {
270-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1));
312+
if (!_d && !_a && (_b = y_1.return)) yield __await(_b.call(y_1));
271313
}
272314
finally { if (e_1) throw e_1.error; }
273315
}
@@ -296,18 +338,25 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
296338
// https://github.com/microsoft/TypeScript/issues/36166
297339
function f7() {
298340
return __asyncGenerator(this, arguments, function* f7_1() {
299-
var e_1, _a;
341+
var _a, e_1, _b, _c;
300342
let y;
301343
for (;;) {
302344
try {
303-
for (var y_1 = (e_1 = void 0, __asyncValues(y)), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
304-
const x = y_1_1.value;
345+
for (var _d = true, y_1 = (e_1 = void 0, __asyncValues(y)), y_1_1; y_1_1 = yield __await(y_1.next()), _a = y_1_1.done, !_a;) {
346+
_c = y_1_1.value;
347+
_d = false;
348+
try {
349+
const x = _c;
350+
}
351+
finally {
352+
_d = true;
353+
}
305354
}
306355
}
307356
catch (e_1_1) { e_1 = { error: e_1_1 }; }
308357
finally {
309358
try {
310-
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1));
359+
if (!_d && !_a && (_b = y_1.return)) yield __await(_b.call(y_1));
311360
}
312361
finally { if (e_1) throw e_1.error; }
313362
}

0 commit comments

Comments
 (0)
Please sign in to comment.