Skip to content

Commit b3dc32f

Browse files
authored
Reset error record in downlevel for-of (#31519)
1 parent 7611c5b commit b3dc32f

File tree

5 files changed

+191
-18
lines changed

5 files changed

+191
-18
lines changed

src/compiler/transformers/es2015.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ namespace ts {
145145
loopOutParameters: LoopOutParameter[];
146146
}
147147

148-
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined) => Statement;
148+
type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement;
149149

150150
// Facts we track as we traverse the tree
151151
const enum HierarchyFacts {
@@ -163,11 +163,12 @@ namespace ts {
163163
ExportedVariableStatement = 1 << 5, // Enclosed in an exported variable statement in the current scope
164164
TopLevel = 1 << 6, // Enclosing block-scoped container is a top-level container
165165
Block = 1 << 7, // Enclosing block-scoped container is a Block
166-
IterationStatement = 1 << 8, // Enclosed in an IterationStatement
166+
IterationStatement = 1 << 8, // Immediately enclosed in an IterationStatement
167167
IterationStatementBlock = 1 << 9, // Enclosing Block is enclosed in an IterationStatement
168-
ForStatement = 1 << 10, // Enclosing block-scoped container is a ForStatement
169-
ForInOrForOfStatement = 1 << 11, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
170-
ConstructorWithCapturedSuper = 1 << 12, // Enclosed in a constructor that captures 'this' for use with 'super'
168+
IterationContainer = 1 << 10, // Enclosed in an outer IterationStatement
169+
ForStatement = 1 << 11, // Enclosing block-scoped container is a ForStatement
170+
ForInOrForOfStatement = 1 << 12, // Enclosing block-scoped container is a ForInStatement or ForOfStatement
171+
ConstructorWithCapturedSuper = 1 << 13, // Enclosed in a constructor that captures 'this' for use with 'super'
171172
// NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
172173
// NOTE: when adding a new ancestor flag, be sure to update the subtree flags below.
173174

@@ -184,11 +185,11 @@ namespace ts {
184185

185186
// A source file is a top-level block scope.
186187
SourceFileIncludes = TopLevel,
187-
SourceFileExcludes = BlockScopeExcludes & ~TopLevel,
188+
SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer,
188189

189190
// Functions, methods, and accessors are both new lexical scopes and new block scopes.
190191
FunctionIncludes = Function | TopLevel,
191-
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper,
192+
FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper | IterationContainer,
192193

193194
AsyncFunctionBodyIncludes = FunctionIncludes | AsyncFunctionBody,
194195
AsyncFunctionBodyExcludes = FunctionExcludes & ~NonStaticClassElement,
@@ -205,16 +206,16 @@ namespace ts {
205206
// 'do' and 'while' statements are not block scopes. We track that the subtree is contained
206207
// within an IterationStatement to indicate whether the embedded statement is an
207208
// IterationStatementBlock.
208-
DoOrWhileStatementIncludes = IterationStatement,
209+
DoOrWhileStatementIncludes = IterationStatement | IterationContainer,
209210
DoOrWhileStatementExcludes = None,
210211

211212
// 'for' statements are new block scopes and have special handling for 'let' declarations.
212-
ForStatementIncludes = IterationStatement | ForStatement,
213+
ForStatementIncludes = IterationStatement | ForStatement | IterationContainer,
213214
ForStatementExcludes = BlockScopeExcludes & ~ForStatement,
214215

215216
// 'for-in' and 'for-of' statements are new block scopes and have special handling for
216217
// 'let' declarations.
217-
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement,
218+
ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement | IterationContainer,
218219
ForInOrForOfStatementExcludes = BlockScopeExcludes & ~ForInOrForOfStatement,
219220

220221
// Blocks (other than function bodies) are new block scopes.
@@ -228,8 +229,8 @@ namespace ts {
228229
// Subtree facts
229230
//
230231

231-
NewTarget = 1 << 13, // Contains a 'new.target' meta-property
232-
CapturedLexicalThis = 1 << 14, // Contains a lexical `this` reference captured by an arrow function.
232+
NewTarget = 1 << 14, // Contains a 'new.target' meta-property
233+
CapturedLexicalThis = 1 << 15, // Contains a lexical `this` reference captured by an arrow function.
233234

234235
//
235236
// Subtree masks
@@ -2227,7 +2228,7 @@ namespace ts {
22272228

22282229
function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) {
22292230
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
2230-
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, convert);
2231+
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert);
22312232
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
22322233
return updated;
22332234
}
@@ -2434,7 +2435,7 @@ namespace ts {
24342435
return restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel);
24352436
}
24362437

2437-
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement {
2438+
function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], ancestorFacts: HierarchyFacts): Statement {
24382439
const expression = visitNode(node.expression, visitor, isExpression);
24392440
const iterator = isIdentifier(expression) ? getGeneratedNameForNode(expression) : createTempVariable(/*recordTempVariable*/ undefined);
24402441
const result = isIdentifier(expression) ? getGeneratedNameForNode(iterator) : createTempVariable(/*recordTempVariable*/ undefined);
@@ -2447,13 +2448,18 @@ namespace ts {
24472448
hoistVariableDeclaration(errorRecord);
24482449
hoistVariableDeclaration(returnMethod);
24492450

2451+
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
2452+
const initializer = ancestorFacts & HierarchyFacts.IterationContainer
2453+
? inlineExpressions([createAssignment(errorRecord, createVoidZero()), values])
2454+
: values;
2455+
24502456
const forStatement = setEmitFlags(
24512457
setTextRange(
24522458
createFor(
24532459
/*initializer*/ setEmitFlags(
24542460
setTextRange(
24552461
createVariableDeclarationList([
2456-
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
2462+
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
24572463
createVariableDeclaration(result, /*type*/ undefined, next)
24582464
]),
24592465
node.expression
@@ -2665,7 +2671,7 @@ namespace ts {
26652671
}
26662672
}
26672673

2668-
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
2674+
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> {
26692675
if (!shouldConvertIterationStatement(node)) {
26702676
let saveAllowedNonLabeledJumps: Jump | undefined;
26712677
if (convertedLoopState) {
@@ -2676,7 +2682,7 @@ namespace ts {
26762682
}
26772683

26782684
const result = convert
2679-
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined)
2685+
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
26802686
: restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);
26812687

26822688
if (convertedLoopState) {
@@ -2708,7 +2714,7 @@ namespace ts {
27082714
let loop: Statement;
27092715
if (bodyFunction) {
27102716
if (convert) {
2711-
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
2717+
loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts);
27122718
}
27132719
else {
27142720
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [ES5For-of37.ts]
2+
// https://github.com/microsoft/TypeScript/issues/30083
3+
4+
for (const i of [0, 1, 2, 3, 4]) {
5+
try {
6+
// Ensure catch binding for the following loop is reset per iteration:
7+
for (const j of [1, 2, 3]) {
8+
if (i === 2) {
9+
throw new Error('ERR');
10+
}
11+
}
12+
console.log(i);
13+
} catch (err) {
14+
console.log('E %s %s', i, err);
15+
}
16+
}
17+
18+
//// [ES5For-of37.js]
19+
// https://github.com/microsoft/TypeScript/issues/30083
20+
var __values = (this && this.__values) || function (o) {
21+
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
22+
if (m) return m.call(o);
23+
return {
24+
next: function () {
25+
if (o && i >= o.length) o = void 0;
26+
return { value: o && o[i++], done: !o };
27+
}
28+
};
29+
};
30+
var e_1, _a, e_2, _b;
31+
try {
32+
for (var _c = __values([0, 1, 2, 3, 4]), _d = _c.next(); !_d.done; _d = _c.next()) {
33+
var i = _d.value;
34+
try {
35+
try {
36+
// Ensure catch binding for the following loop is reset per iteration:
37+
for (var _e = (e_2 = void 0, __values([1, 2, 3])), _f = _e.next(); !_f.done; _f = _e.next()) {
38+
var j = _f.value;
39+
if (i === 2) {
40+
throw new Error('ERR');
41+
}
42+
}
43+
}
44+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
45+
finally {
46+
try {
47+
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e);
48+
}
49+
finally { if (e_2) throw e_2.error; }
50+
}
51+
console.log(i);
52+
}
53+
catch (err) {
54+
console.log('E %s %s', i, err);
55+
}
56+
}
57+
}
58+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
59+
finally {
60+
try {
61+
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
62+
}
63+
finally { if (e_1) throw e_1.error; }
64+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of37.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/30083
3+
4+
for (const i of [0, 1, 2, 3, 4]) {
5+
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))
6+
7+
try {
8+
// Ensure catch binding for the following loop is reset per iteration:
9+
for (const j of [1, 2, 3]) {
10+
>j : Symbol(j, Decl(ES5For-of37.ts, 5, 18))
11+
12+
if (i === 2) {
13+
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))
14+
15+
throw new Error('ERR');
16+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
17+
}
18+
}
19+
console.log(i);
20+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
21+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
22+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
23+
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))
24+
25+
} catch (err) {
26+
>err : Symbol(err, Decl(ES5For-of37.ts, 11, 13))
27+
28+
console.log('E %s %s', i, err);
29+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
30+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
31+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
32+
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))
33+
>err : Symbol(err, Decl(ES5For-of37.ts, 11, 13))
34+
}
35+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of37.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/30083
3+
4+
for (const i of [0, 1, 2, 3, 4]) {
5+
>i : number
6+
>[0, 1, 2, 3, 4] : number[]
7+
>0 : 0
8+
>1 : 1
9+
>2 : 2
10+
>3 : 3
11+
>4 : 4
12+
13+
try {
14+
// Ensure catch binding for the following loop is reset per iteration:
15+
for (const j of [1, 2, 3]) {
16+
>j : number
17+
>[1, 2, 3] : number[]
18+
>1 : 1
19+
>2 : 2
20+
>3 : 3
21+
22+
if (i === 2) {
23+
>i === 2 : boolean
24+
>i : number
25+
>2 : 2
26+
27+
throw new Error('ERR');
28+
>new Error('ERR') : Error
29+
>Error : ErrorConstructor
30+
>'ERR' : "ERR"
31+
}
32+
}
33+
console.log(i);
34+
>console.log(i) : void
35+
>console.log : (message?: any, ...optionalParams: any[]) => void
36+
>console : Console
37+
>log : (message?: any, ...optionalParams: any[]) => void
38+
>i : number
39+
40+
} catch (err) {
41+
>err : any
42+
43+
console.log('E %s %s', i, err);
44+
>console.log('E %s %s', i, err) : void
45+
>console.log : (message?: any, ...optionalParams: any[]) => void
46+
>console : Console
47+
>log : (message?: any, ...optionalParams: any[]) => void
48+
>'E %s %s' : "E %s %s"
49+
>i : number
50+
>err : any
51+
}
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @downlevelIteration: true
2+
// https://github.com/microsoft/TypeScript/issues/30083
3+
4+
for (const i of [0, 1, 2, 3, 4]) {
5+
try {
6+
// Ensure catch binding for the following loop is reset per iteration:
7+
for (const j of [1, 2, 3]) {
8+
if (i === 2) {
9+
throw new Error('ERR');
10+
}
11+
}
12+
console.log(i);
13+
} catch (err) {
14+
console.log('E %s %s', i, err);
15+
}
16+
}

0 commit comments

Comments
 (0)