Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset error record in downlevel for-of #31519

Merged
merged 1 commit into from May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 24 additions & 18 deletions src/compiler/transformers/es2015.ts
Expand Up @@ -145,7 +145,7 @@ namespace ts {
loopOutParameters: LoopOutParameter[];
}

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

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

Expand All @@ -184,11 +185,11 @@ namespace ts {

// A source file is a top-level block scope.
SourceFileIncludes = TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel,
SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer,

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

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

// 'for' statements are new block scopes and have special handling for 'let' declarations.
ForStatementIncludes = IterationStatement | ForStatement,
ForStatementIncludes = IterationStatement | ForStatement | IterationContainer,
ForStatementExcludes = BlockScopeExcludes & ~ForStatement,

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

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

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

//
// Subtree masks
Expand Down Expand Up @@ -2227,7 +2228,7 @@ namespace ts {

function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) {
const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, convert);
const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert);
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return updated;
}
Expand Down Expand Up @@ -2434,7 +2435,7 @@ namespace ts {
return restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel);
}

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

// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
const initializer = ancestorFacts & HierarchyFacts.IterationContainer
? inlineExpressions([createAssignment(errorRecord, createVoidZero()), values])
: values;

const forStatement = setEmitFlags(
setTextRange(
createFor(
/*initializer*/ setEmitFlags(
setTextRange(
createVariableDeclarationList([
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, values), node.expression),
setTextRange(createVariableDeclaration(iterator, /*type*/ undefined, initializer), node.expression),
createVariableDeclaration(result, /*type*/ undefined, next)
]),
node.expression
Expand Down Expand Up @@ -2665,7 +2671,7 @@ namespace ts {
}
}

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

const result = convert
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined)
? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts)
: restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel);

if (convertedLoopState) {
Expand Down Expand Up @@ -2708,7 +2714,7 @@ namespace ts {
let loop: Statement;
if (bodyFunction) {
if (convert) {
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts);
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
Expand Down
64 changes: 64 additions & 0 deletions tests/baselines/reference/ES5For-of37.js
@@ -0,0 +1,64 @@
//// [ES5For-of37.ts]
// https://github.com/microsoft/TypeScript/issues/30083

for (const i of [0, 1, 2, 3, 4]) {
try {
// Ensure catch binding for the following loop is reset per iteration:
for (const j of [1, 2, 3]) {
if (i === 2) {
throw new Error('ERR');
}
}
console.log(i);
} catch (err) {
console.log('E %s %s', i, err);
}
}

//// [ES5For-of37.js]
// https://github.com/microsoft/TypeScript/issues/30083
var __values = (this && this.__values) || function (o) {
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
if (m) return m.call(o);
return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
};
var e_1, _a, e_2, _b;
try {
for (var _c = __values([0, 1, 2, 3, 4]), _d = _c.next(); !_d.done; _d = _c.next()) {
var i = _d.value;
try {
try {
// Ensure catch binding for the following loop is reset per iteration:
for (var _e = (e_2 = void 0, __values([1, 2, 3])), _f = _e.next(); !_f.done; _f = _e.next()) {
var j = _f.value;
if (i === 2) {
throw new Error('ERR');
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e["return"])) _b.call(_e);
}
finally { if (e_2) throw e_2.error; }
}
console.log(i);
}
catch (err) {
console.log('E %s %s', i, err);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c["return"])) _a.call(_c);
}
finally { if (e_1) throw e_1.error; }
}
35 changes: 35 additions & 0 deletions tests/baselines/reference/ES5For-of37.symbols
@@ -0,0 +1,35 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of37.ts ===
// https://github.com/microsoft/TypeScript/issues/30083

for (const i of [0, 1, 2, 3, 4]) {
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))

try {
// Ensure catch binding for the following loop is reset per iteration:
for (const j of [1, 2, 3]) {
>j : Symbol(j, Decl(ES5For-of37.ts, 5, 18))

if (i === 2) {
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))

throw new Error('ERR');
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
}
console.log(i);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))

} catch (err) {
>err : Symbol(err, Decl(ES5For-of37.ts, 11, 13))

console.log('E %s %s', i, err);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>i : Symbol(i, Decl(ES5For-of37.ts, 2, 10))
>err : Symbol(err, Decl(ES5For-of37.ts, 11, 13))
}
}
52 changes: 52 additions & 0 deletions tests/baselines/reference/ES5For-of37.types
@@ -0,0 +1,52 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of37.ts ===
// https://github.com/microsoft/TypeScript/issues/30083

for (const i of [0, 1, 2, 3, 4]) {
>i : number
>[0, 1, 2, 3, 4] : number[]
>0 : 0
>1 : 1
>2 : 2
>3 : 3
>4 : 4

try {
// Ensure catch binding for the following loop is reset per iteration:
for (const j of [1, 2, 3]) {
>j : number
>[1, 2, 3] : number[]
>1 : 1
>2 : 2
>3 : 3

if (i === 2) {
>i === 2 : boolean
>i : number
>2 : 2

throw new Error('ERR');
>new Error('ERR') : Error
>Error : ErrorConstructor
>'ERR' : "ERR"
}
}
console.log(i);
>console.log(i) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>i : number

} catch (err) {
>err : any

console.log('E %s %s', i, err);
>console.log('E %s %s', i, err) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>'E %s %s' : "E %s %s"
>i : number
>err : any
}
}
16 changes: 16 additions & 0 deletions tests/cases/conformance/statements/for-ofStatements/ES5For-of37.ts
@@ -0,0 +1,16 @@
// @downlevelIteration: true
// https://github.com/microsoft/TypeScript/issues/30083

for (const i of [0, 1, 2, 3, 4]) {
try {
// Ensure catch binding for the following loop is reset per iteration:
for (const j of [1, 2, 3]) {
if (i === 2) {
throw new Error('ERR');
}
}
console.log(i);
} catch (err) {
console.log('E %s %s', i, err);
}
}