From fd8000dd59752e38ff84e9d0180729bbd6294e8c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 24 Feb 2020 10:55:13 -0800 Subject: [PATCH] Fix class emit in converted loop body (#36795) --- src/compiler/transformers/es2015.ts | 14 +++++++- .../reference/classInConvertedLoopES5.js | 27 ++++++++++++++ .../reference/classInConvertedLoopES5.symbols | 25 +++++++++++++ .../reference/classInConvertedLoopES5.types | 35 +++++++++++++++++++ .../cases/compiler/classInConvertedLoopES5.ts | 10 ++++++ 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/classInConvertedLoopES5.js create mode 100644 tests/baselines/reference/classInConvertedLoopES5.symbols create mode 100644 tests/baselines/reference/classInConvertedLoopES5.types create mode 100644 tests/cases/compiler/classInConvertedLoopES5.ts diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index d4bf9605a2e..4d62bbbe405 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -2002,10 +2002,16 @@ namespace ts { return visitEachChild(node, visitor, context); } + function isVariableStatementOfTypeScriptClassWrapper(node: VariableStatement) { + return node.declarationList.declarations.length === 1 + && !!node.declarationList.declarations[0].initializer + && !!(getEmitFlags(node.declarationList.declarations[0].initializer) & EmitFlags.TypeScriptClassWrapper); + } + function visitVariableStatement(node: VariableStatement): Statement | undefined { const ancestorFacts = enterSubtree(HierarchyFacts.None, hasModifier(node, ModifierFlags.Export) ? HierarchyFacts.ExportedVariableStatement : HierarchyFacts.None); let updated: Statement | undefined; - if (convertedLoopState && (node.declarationList.flags & NodeFlags.BlockScoped) === 0) { + if (convertedLoopState && (node.declarationList.flags & NodeFlags.BlockScoped) === 0 && !isVariableStatementOfTypeScriptClassWrapper(node)) { // we are inside a converted loop - hoist variable declarations let assignments: Expression[] | undefined; for (const decl of node.declarationList.declarations) { @@ -3606,7 +3612,13 @@ namespace ts { // The class statements are the statements generated by visiting the first statement with initializer of the // body (1), while all other statements are added to remainingStatements (2) const isVariableStatementWithInitializer = (stmt: Statement) => isVariableStatement(stmt) && !!first(stmt.declarationList.declarations).initializer; + + // visit the class body statements outside of any converted loop body. + const savedConvertedLoopState = convertedLoopState; + convertedLoopState = undefined; const bodyStatements = visitNodes(body.statements, visitor, isStatement); + convertedLoopState = savedConvertedLoopState; + const classStatements = filter(bodyStatements, isVariableStatementWithInitializer); const remainingStatements = filter(bodyStatements, stmt => !isVariableStatementWithInitializer(stmt)); const varStatement = cast(first(classStatements), isVariableStatement); diff --git a/tests/baselines/reference/classInConvertedLoopES5.js b/tests/baselines/reference/classInConvertedLoopES5.js new file mode 100644 index 00000000000..eb7f3d4ebf5 --- /dev/null +++ b/tests/baselines/reference/classInConvertedLoopES5.js @@ -0,0 +1,27 @@ +//// [classInConvertedLoopES5.ts] +const classesByRow: Record = {}; +for (const row of ['1', '2', '3', '4', '5']) { + class RowClass { + row = row; + static factory = () => new RowClass(); + } + + classesByRow[row] = RowClass; +} + +//// [classInConvertedLoopES5.js] +var classesByRow = {}; +var _loop_1 = function (row) { + var RowClass = /** @class */ (function () { + function RowClass() { + this.row = row; + } + RowClass.factory = function () { return new RowClass(); }; + return RowClass; + }()); + classesByRow[row] = RowClass; +}; +for (var _i = 0, _a = ['1', '2', '3', '4', '5']; _i < _a.length; _i++) { + var row = _a[_i]; + _loop_1(row); +} diff --git a/tests/baselines/reference/classInConvertedLoopES5.symbols b/tests/baselines/reference/classInConvertedLoopES5.symbols new file mode 100644 index 00000000000..20a8a6a36a8 --- /dev/null +++ b/tests/baselines/reference/classInConvertedLoopES5.symbols @@ -0,0 +1,25 @@ +=== tests/cases/compiler/classInConvertedLoopES5.ts === +const classesByRow: Record = {}; +>classesByRow : Symbol(classesByRow, Decl(classInConvertedLoopES5.ts, 0, 5)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +for (const row of ['1', '2', '3', '4', '5']) { +>row : Symbol(row, Decl(classInConvertedLoopES5.ts, 1, 10)) + + class RowClass { +>RowClass : Symbol(RowClass, Decl(classInConvertedLoopES5.ts, 1, 46)) + + row = row; +>row : Symbol(RowClass.row, Decl(classInConvertedLoopES5.ts, 2, 18)) +>row : Symbol(row, Decl(classInConvertedLoopES5.ts, 1, 10)) + + static factory = () => new RowClass(); +>factory : Symbol(RowClass.factory, Decl(classInConvertedLoopES5.ts, 3, 14)) +>RowClass : Symbol(RowClass, Decl(classInConvertedLoopES5.ts, 1, 46)) + } + + classesByRow[row] = RowClass; +>classesByRow : Symbol(classesByRow, Decl(classInConvertedLoopES5.ts, 0, 5)) +>row : Symbol(row, Decl(classInConvertedLoopES5.ts, 1, 10)) +>RowClass : Symbol(RowClass, Decl(classInConvertedLoopES5.ts, 1, 46)) +} diff --git a/tests/baselines/reference/classInConvertedLoopES5.types b/tests/baselines/reference/classInConvertedLoopES5.types new file mode 100644 index 00000000000..63a4ed8f359 --- /dev/null +++ b/tests/baselines/reference/classInConvertedLoopES5.types @@ -0,0 +1,35 @@ +=== tests/cases/compiler/classInConvertedLoopES5.ts === +const classesByRow: Record = {}; +>classesByRow : Record +>{} : {} + +for (const row of ['1', '2', '3', '4', '5']) { +>row : string +>['1', '2', '3', '4', '5'] : string[] +>'1' : "1" +>'2' : "2" +>'3' : "3" +>'4' : "4" +>'5' : "5" + + class RowClass { +>RowClass : RowClass + + row = row; +>row : string +>row : string + + static factory = () => new RowClass(); +>factory : () => RowClass +>() => new RowClass() : () => RowClass +>new RowClass() : RowClass +>RowClass : typeof RowClass + } + + classesByRow[row] = RowClass; +>classesByRow[row] = RowClass : typeof RowClass +>classesByRow[row] : object +>classesByRow : Record +>row : string +>RowClass : typeof RowClass +} diff --git a/tests/cases/compiler/classInConvertedLoopES5.ts b/tests/cases/compiler/classInConvertedLoopES5.ts new file mode 100644 index 00000000000..bc2801f5877 --- /dev/null +++ b/tests/cases/compiler/classInConvertedLoopES5.ts @@ -0,0 +1,10 @@ +// @target: es5 +const classesByRow: Record = {}; +for (const row of ['1', '2', '3', '4', '5']) { + class RowClass { + row = row; + static factory = () => new RowClass(); + } + + classesByRow[row] = RowClass; +} \ No newline at end of file