Skip to content

Commit 968943f

Browse files
authored
Reset error variable in downlevel for-await-of loop (#38170)
* Rename forAwait tests * Reset error var in for-await loop
1 parent d28e38f commit 968943f

17 files changed

+532
-207
lines changed

src/compiler/transformers/es2018.ts

Lines changed: 137 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@ namespace ts {
55
AsyncMethodsWithSuper = 1 << 0
66
}
77

8+
// Facts we track as we traverse the tree
9+
const enum HierarchyFacts {
10+
None = 0,
11+
12+
//
13+
// Ancestor facts
14+
//
15+
16+
HasLexicalThis = 1 << 0,
17+
IterationContainer = 1 << 1,
18+
// NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
19+
20+
//
21+
// Ancestor masks
22+
//
23+
24+
AncestorFactsMask = (IterationContainer << 1) - 1,
25+
26+
SourceFileIncludes = HasLexicalThis,
27+
SourceFileExcludes = IterationContainer,
28+
StrictModeSourceFileIncludes = None,
29+
30+
ClassOrFunctionIncludes = HasLexicalThis,
31+
ClassOrFunctionExcludes = IterationContainer,
32+
33+
ArrowFunctionIncludes = None,
34+
ArrowFunctionExcludes = ClassOrFunctionExcludes,
35+
36+
IterationStatementIncludes = IterationContainer,
37+
IterationStatementExcludes = None,
38+
}
39+
840
export function transformES2018(context: TransformationContext) {
941
const {
1042
resumeLexicalEnvironment,
@@ -26,7 +58,7 @@ namespace ts {
2658
let enabledSubstitutions: ESNextSubstitutionFlags;
2759
let enclosingFunctionFlags: FunctionFlags;
2860
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
29-
let hasLexicalThis: boolean;
61+
let hierarchyFacts: HierarchyFacts = 0;
3062

3163
let currentSourceFile: SourceFile;
3264
let taggedTemplateStringDeclarations: VariableDeclaration[];
@@ -40,6 +72,30 @@ namespace ts {
4072

4173
return chainBundle(transformSourceFile);
4274

75+
function affectsSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
76+
return hierarchyFacts !== (hierarchyFacts & ~excludeFacts | includeFacts);
77+
}
78+
79+
/**
80+
* Sets the `HierarchyFacts` for this node prior to visiting this node's subtree, returning the facts set prior to modification.
81+
* @param excludeFacts The existing `HierarchyFacts` to reset before visiting the subtree.
82+
* @param includeFacts The new `HierarchyFacts` to set before visiting the subtree.
83+
*/
84+
function enterSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
85+
const ancestorFacts = hierarchyFacts;
86+
hierarchyFacts = (hierarchyFacts & ~excludeFacts | includeFacts) & HierarchyFacts.AncestorFactsMask;
87+
return ancestorFacts;
88+
}
89+
90+
/**
91+
* Restores the `HierarchyFacts` for this node's ancestor after visiting this node's
92+
* subtree.
93+
* @param ancestorFacts The `HierarchyFacts` of the ancestor to restore after visiting the subtree.
94+
*/
95+
function exitSubtree(ancestorFacts: HierarchyFacts) {
96+
hierarchyFacts = ancestorFacts;
97+
}
98+
4399
function recordTaggedTemplateString(temp: Identifier) {
44100
taggedTemplateStringDeclarations = append(
45101
taggedTemplateStringDeclarations,
@@ -75,11 +131,11 @@ namespace ts {
75131
return node;
76132
}
77133

78-
function doWithLexicalThis<T, U>(cb: (value: T) => U, value: T) {
79-
if (!hasLexicalThis) {
80-
hasLexicalThis = true;
134+
function doWithHierarchyFacts<T, U>(cb: (value: T) => U, value: T, excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
135+
if (affectsSubtree(excludeFacts, includeFacts)) {
136+
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
81137
const result = cb(value);
82-
hasLexicalThis = false;
138+
exitSubtree(ancestorFacts);
83139
return result;
84140
}
85141
return cb(value);
@@ -112,26 +168,66 @@ namespace ts {
112168
return visitVariableStatement(node as VariableStatement);
113169
case SyntaxKind.VariableDeclaration:
114170
return visitVariableDeclaration(node as VariableDeclaration);
171+
case SyntaxKind.DoStatement:
172+
case SyntaxKind.WhileStatement:
173+
case SyntaxKind.ForInStatement:
174+
return doWithHierarchyFacts(
175+
visitDefault,
176+
node,
177+
HierarchyFacts.IterationStatementExcludes,
178+
HierarchyFacts.IterationStatementIncludes);
115179
case SyntaxKind.ForOfStatement:
116180
return visitForOfStatement(node as ForOfStatement, /*outermostLabeledStatement*/ undefined);
117181
case SyntaxKind.ForStatement:
118-
return visitForStatement(node as ForStatement);
182+
return doWithHierarchyFacts(
183+
visitForStatement,
184+
node as ForStatement,
185+
HierarchyFacts.IterationStatementExcludes,
186+
HierarchyFacts.IterationStatementIncludes);
119187
case SyntaxKind.VoidExpression:
120188
return visitVoidExpression(node as VoidExpression);
121189
case SyntaxKind.Constructor:
122-
return doWithLexicalThis(visitConstructorDeclaration, node as ConstructorDeclaration);
190+
return doWithHierarchyFacts(
191+
visitConstructorDeclaration,
192+
node as ConstructorDeclaration,
193+
HierarchyFacts.ClassOrFunctionExcludes,
194+
HierarchyFacts.ClassOrFunctionIncludes);
123195
case SyntaxKind.MethodDeclaration:
124-
return doWithLexicalThis(visitMethodDeclaration, node as MethodDeclaration);
196+
return doWithHierarchyFacts(
197+
visitMethodDeclaration,
198+
node as MethodDeclaration,
199+
HierarchyFacts.ClassOrFunctionExcludes,
200+
HierarchyFacts.ClassOrFunctionIncludes);
125201
case SyntaxKind.GetAccessor:
126-
return doWithLexicalThis(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
202+
return doWithHierarchyFacts(
203+
visitGetAccessorDeclaration,
204+
node as GetAccessorDeclaration,
205+
HierarchyFacts.ClassOrFunctionExcludes,
206+
HierarchyFacts.ClassOrFunctionIncludes);
127207
case SyntaxKind.SetAccessor:
128-
return doWithLexicalThis(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
208+
return doWithHierarchyFacts(
209+
visitSetAccessorDeclaration,
210+
node as SetAccessorDeclaration,
211+
HierarchyFacts.ClassOrFunctionExcludes,
212+
HierarchyFacts.ClassOrFunctionIncludes);
129213
case SyntaxKind.FunctionDeclaration:
130-
return doWithLexicalThis(visitFunctionDeclaration, node as FunctionDeclaration);
214+
return doWithHierarchyFacts(
215+
visitFunctionDeclaration,
216+
node as FunctionDeclaration,
217+
HierarchyFacts.ClassOrFunctionExcludes,
218+
HierarchyFacts.ClassOrFunctionIncludes);
131219
case SyntaxKind.FunctionExpression:
132-
return doWithLexicalThis(visitFunctionExpression, node as FunctionExpression);
220+
return doWithHierarchyFacts(
221+
visitFunctionExpression,
222+
node as FunctionExpression,
223+
HierarchyFacts.ClassOrFunctionExcludes,
224+
HierarchyFacts.ClassOrFunctionIncludes);
133225
case SyntaxKind.ArrowFunction:
134-
return visitArrowFunction(node as ArrowFunction);
226+
return doWithHierarchyFacts(
227+
visitArrowFunction,
228+
node as ArrowFunction,
229+
HierarchyFacts.ArrowFunctionExcludes,
230+
HierarchyFacts.ArrowFunctionIncludes);
135231
case SyntaxKind.Parameter:
136232
return visitParameter(node as ParameterDeclaration);
137233
case SyntaxKind.ExpressionStatement:
@@ -152,7 +248,11 @@ namespace ts {
152248
return visitEachChild(node, visitor, context);
153249
case SyntaxKind.ClassDeclaration:
154250
case SyntaxKind.ClassExpression:
155-
return doWithLexicalThis(visitDefault, node);
251+
return doWithHierarchyFacts(
252+
visitDefault,
253+
node,
254+
HierarchyFacts.ClassOrFunctionExcludes,
255+
HierarchyFacts.ClassOrFunctionIncludes);
156256
default:
157257
return visitEachChild(node, visitor, context);
158258
}
@@ -231,7 +331,7 @@ namespace ts {
231331
if (statement.kind === SyntaxKind.ForOfStatement && (<ForOfStatement>statement).awaitModifier) {
232332
return visitForOfStatement(<ForOfStatement>statement, node);
233333
}
234-
return restoreEnclosingLabel(visitEachChild(statement, visitor, context), node);
334+
return restoreEnclosingLabel(visitNode(statement, visitor, isStatement, liftToBlock), node);
235335
}
236336
return visitEachChild(node, visitor, context);
237337
}
@@ -311,14 +411,20 @@ namespace ts {
311411
}
312412

313413
function visitSourceFile(node: SourceFile): SourceFile {
414+
const ancestorFacts = enterSubtree(
415+
HierarchyFacts.SourceFileExcludes,
416+
isEffectiveStrictModeSourceFile(node, compilerOptions) ?
417+
HierarchyFacts.StrictModeSourceFileIncludes :
418+
HierarchyFacts.SourceFileIncludes);
314419
exportedVariableStatement = false;
315-
hasLexicalThis = !isEffectiveStrictModeSourceFile(node, compilerOptions);
316420
const visited = visitEachChild(node, visitor, context);
317421
const statement = concatenate(visited.statements, taggedTemplateStringDeclarations && [
318422
createVariableStatement(/*modifiers*/ undefined,
319423
createVariableDeclarationList(taggedTemplateStringDeclarations))
320424
]);
321-
return updateSourceFileNode(visited, setTextRange(createNodeArray(statement), node.statements));
425+
const result = updateSourceFileNode(visited, setTextRange(createNodeArray(statement), node.statements));
426+
exitSubtree(ancestorFacts);
427+
return result;
322428
}
323429

324430
function visitTaggedTemplateExpression(node: TaggedTemplateExpression) {
@@ -441,15 +547,15 @@ namespace ts {
441547
* @param node A ForOfStatement.
442548
*/
443549
function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> {
550+
const ancestorFacts = enterSubtree(HierarchyFacts.IterationStatementExcludes, HierarchyFacts.IterationStatementIncludes);
444551
if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
445552
node = transformForOfStatementWithObjectRest(node);
446553
}
447-
if (node.awaitModifier) {
448-
return transformForAwaitOfStatement(node, outermostLabeledStatement);
449-
}
450-
else {
451-
return restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement);
452-
}
554+
const result = node.awaitModifier ?
555+
transformForAwaitOfStatement(node, outermostLabeledStatement, ancestorFacts) :
556+
restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement);
557+
exitSubtree(ancestorFacts);
558+
return result;
453559
}
454560

455561
function transformForOfStatementWithObjectRest(node: ForOfStatement) {
@@ -528,7 +634,7 @@ namespace ts {
528634
: createAwait(expression);
529635
}
530636

531-
function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined) {
637+
function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts) {
532638
const expression = visitNode(node.expression, visitor, isExpression);
533639
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
534640
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
@@ -544,13 +650,18 @@ namespace ts {
544650
hoistVariableDeclaration(errorRecord);
545651
hoistVariableDeclaration(returnMethod);
546652

653+
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
654+
const initializer = ancestorFacts & HierarchyFacts.IterationContainer ?
655+
inlineExpressions([createAssignment(errorRecord, createVoidZero()), callValues]) :
656+
callValues;
657+
547658
const forStatement = setEmitFlags(
548659
setTextRange(
549660
createFor(
550661
/*initializer*/ setEmitFlags(
551662
setTextRange(
552663
createVariableDeclarationList([
553-
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, callValues), node.expression),
664+
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
554665
createVariableDeclaration(result)
555666
]),
556667
node.expression
@@ -809,7 +920,7 @@ namespace ts {
809920
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
810921
)
811922
),
812-
hasLexicalThis
923+
!!(hierarchyFacts & HierarchyFacts.HasLexicalThis)
813924
)
814925
);
815926

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//// [tests/cases/conformance/emitter/es2015/forAwait/emitter.forAwait.es2015.ts] ////
1+
//// [tests/cases/conformance/statements/for-await-ofStatements/emitter.forAwait.ts] ////
22

33
//// [file1.ts]
44
async function f1() {
@@ -40,6 +40,15 @@ async function* f6() {
4040
continue outer;
4141
}
4242
}
43+
//// [file7.ts]
44+
// https://github.com/microsoft/TypeScript/issues/36166
45+
async function* f7() {
46+
let y: any;
47+
for (;;) {
48+
for await (const x of y) {
49+
}
50+
}
51+
}
4352

4453
//// [file1.js]
4554
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
@@ -264,3 +273,44 @@ function f6() {
264273
}
265274
});
266275
}
276+
//// [file7.js]
277+
var __asyncValues = (this && this.__asyncValues) || function (o) {
278+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
279+
var m = o[Symbol.asyncIterator], i;
280+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
281+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
282+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
283+
};
284+
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
285+
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
286+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
287+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
288+
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
289+
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
290+
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
291+
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
292+
function fulfill(value) { resume("next", value); }
293+
function reject(value) { resume("throw", value); }
294+
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
295+
};
296+
// https://github.com/microsoft/TypeScript/issues/36166
297+
function f7() {
298+
return __asyncGenerator(this, arguments, function* f7_1() {
299+
var e_1, _a;
300+
let y;
301+
for (;;) {
302+
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;
305+
}
306+
}
307+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
308+
finally {
309+
try {
310+
if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1));
311+
}
312+
finally { if (e_1) throw e_1.error; }
313+
}
314+
}
315+
});
316+
}

0 commit comments

Comments
 (0)