From b21a7399e563011b9cf861d30694a3f033c953cf Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Sat, 24 Sep 2016 15:35:54 -0700 Subject: [PATCH 1/3] Fix downlevel async hoisting --- src/compiler/transformers/generators.ts | 9 +++++++-- tests/baselines/reference/asyncArrowFunction7_es5.js | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index d1e2b6033a6..f05a012378a 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -573,11 +573,11 @@ namespace ts { operationLocations = undefined; state = createTempVariable(/*recordTempVariable*/ undefined); - const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); - // Build the generator startLexicalEnvironment(); + const statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false, visitor); + transformAndEmitStatements(body.statements, statementOffset); const buildResult = build(); @@ -615,6 +615,11 @@ namespace ts { return undefined; } else { + // Do not hoist custom prologues. + if (node.emitFlags & NodeEmitFlags.CustomPrologue) { + return node; + } + for (const variable of node.declarationList.declarations) { hoistVariableDeclaration(variable.name); } diff --git a/tests/baselines/reference/asyncArrowFunction7_es5.js b/tests/baselines/reference/asyncArrowFunction7_es5.js index 19e589fa542..f2edb2b6537 100644 --- a/tests/baselines/reference/asyncArrowFunction7_es5.js +++ b/tests/baselines/reference/asyncArrowFunction7_es5.js @@ -9,7 +9,7 @@ var bar = async (): Promise => { //// [asyncArrowFunction7_es5.js] var _this = this; var bar = function () { return __awaiter(_this, void 0, void 0, function () { - _this = this; + var _this = this; var foo; return __generator(this, function (_a) { foo = function (a) { @@ -22,4 +22,4 @@ var bar = function () { return __awaiter(_this, void 0, void 0, function () { }; return [2 /*return*/]; }); -}); var _this; }; +}); }; From 7f98d3dbb2ef6a1b21e26e861190188fb02d0fec Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Sat, 24 Sep 2016 16:07:13 -0700 Subject: [PATCH 2/3] Fix some issues with module ES6/target ES5 --- src/compiler/transformers/destructuring.ts | 25 +++++++++++-- src/compiler/transformers/es6.ts | 37 ++++++++++++++++--- .../reference/es6modulekindWithES5Target6.js | 6 +-- .../functionsWithModifiersInBlocks1.js | 2 +- .../reference/moduleElementsInWrongContext.js | 2 +- .../moduleElementsInWrongContext2.js | 2 +- .../parserModifierOnStatementInBlock3.js | 2 +- .../parserModifierOnStatementInBlock4.js | 2 +- 8 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index c6079ee1378..a66f5e21d78 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -126,14 +126,22 @@ namespace ts { context: TransformationContext, node: VariableDeclaration, value?: Expression, - visitor?: (node: Node) => VisitResult) { + visitor?: (node: Node) => VisitResult, + recordTempVariable?: (node: Identifier) => void) { const declarations: VariableDeclaration[] = []; + let pendingAssignments: Expression[]; flattenDestructuring(context, node, value, node, emitAssignment, emitTempVariableAssignment, visitor); return declarations; function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) { + if (pendingAssignments) { + pendingAssignments.push(value); + value = inlineExpressions(pendingAssignments); + pendingAssignments = undefined; + } + const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location); declaration.original = original; @@ -146,8 +154,19 @@ namespace ts { } function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(/*recordTempVariable*/ undefined); - emitAssignment(name, value, location, /*original*/ undefined); + const name = createTempVariable(recordTempVariable); + if (recordTempVariable) { + const assignment = createAssignment(name, value, location); + if (pendingAssignments) { + pendingAssignments.push(assignment); + } + else { + pendingAssignments = [assignment]; + } + } + else { + emitAssignment(name, value, location, /*original*/ undefined); + } return name; } } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 51a6a088fb7..128692eed85 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -163,6 +163,7 @@ namespace ts { let currentText: string; let currentParent: Node; let currentNode: Node; + let enclosingVariableStatement: VariableStatement; let enclosingBlockScopeContainer: Node; let enclosingBlockScopeContainerParent: Node; let containingNonArrowFunction: FunctionLikeDeclaration | ClassElement; @@ -210,6 +211,7 @@ namespace ts { const savedSuperScopeContainer = superScopeContainer; const savedCurrentParent = currentParent; const savedCurrentNode = currentNode; + const savedEnclosingVariableStatement = enclosingVariableStatement; const savedEnclosingBlockScopeContainer = enclosingBlockScopeContainer; const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent; @@ -227,6 +229,7 @@ namespace ts { superScopeContainer = savedSuperScopeContainer; currentParent = savedCurrentParent; currentNode = savedCurrentNode; + enclosingVariableStatement = savedEnclosingVariableStatement; enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer; enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent; return visited; @@ -320,7 +323,7 @@ namespace ts { return visitFunctionExpression(node); case SyntaxKind.VariableDeclaration: - return visitVariableDeclaration(node, /*offset*/ undefined); + return visitVariableDeclaration(node); case SyntaxKind.Identifier: return visitIdentifier(node); @@ -432,6 +435,25 @@ namespace ts { } break; } + + // keep track of the enclosing variable statement when in the context of + // variable statements, variable declarations, binding elements, and binding + // patterns. + switch (currentParent.kind) { + case SyntaxKind.VariableStatement: + enclosingVariableStatement = currentParent; + break; + + case SyntaxKind.VariableDeclarationList: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + break; + + default: + enclosingVariableStatement = undefined; + } } } @@ -1334,7 +1356,7 @@ namespace ts { return setOriginalNode( createFunctionDeclaration( /*decorators*/ undefined, - /*modifiers*/ undefined, + node.modifiers, node.asteriskToken, node.name, /*typeParameters*/ undefined, @@ -1663,13 +1685,13 @@ namespace ts { * * @param node A VariableDeclaration node. */ - function visitVariableDeclarationInLetDeclarationList(node: VariableDeclaration, offset: number) { + function visitVariableDeclarationInLetDeclarationList(node: VariableDeclaration) { // For binding pattern names that lack initializers there is no point to emit // explicit initializer since downlevel codegen for destructuring will fail // in the absence of initializer so all binding elements will say uninitialized const name = node.name; if (isBindingPattern(name)) { - return visitVariableDeclaration(node, offset); + return visitVariableDeclaration(node); } if (!node.initializer && shouldEmitExplicitInitializerForLetDeclaration(node)) { @@ -1686,10 +1708,13 @@ namespace ts { * * @param node A VariableDeclaration node. */ - function visitVariableDeclaration(node: VariableDeclaration, offset: number): VisitResult { + function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern. if (isBindingPattern(node.name)) { - return flattenVariableDestructuring(context, node, /*value*/ undefined, visitor); + const recordTempVariablesInLine = !enclosingVariableStatement + || !hasModifier(enclosingVariableStatement, ModifierFlags.Export); + return flattenVariableDestructuring(context, node, /*value*/ undefined, visitor, + recordTempVariablesInLine ? undefined : hoistVariableDeclaration); } return visitEachChild(node, visitor, context); diff --git a/tests/baselines/reference/es6modulekindWithES5Target6.js b/tests/baselines/reference/es6modulekindWithES5Target6.js index 6f8de9db816..d305c60e54e 100644 --- a/tests/baselines/reference/es6modulekindWithES5Target6.js +++ b/tests/baselines/reference/es6modulekindWithES5Target6.js @@ -11,15 +11,15 @@ export default function f3(d = 0) { //// [es6modulekindWithES5Target6.js] -function f1(d) { +export function f1(d) { if (d === void 0) { d = 0; } } -function f2() { +export function f2() { var arg = []; for (var _i = 0; _i < arguments.length; _i++) { arg[_i - 0] = arguments[_i]; } } -function f3(d) { +export default function f3(d) { if (d === void 0) { d = 0; } } diff --git a/tests/baselines/reference/functionsWithModifiersInBlocks1.js b/tests/baselines/reference/functionsWithModifiersInBlocks1.js index 3edd17321cb..873411afd2a 100644 --- a/tests/baselines/reference/functionsWithModifiersInBlocks1.js +++ b/tests/baselines/reference/functionsWithModifiersInBlocks1.js @@ -7,5 +7,5 @@ //// [functionsWithModifiersInBlocks1.js] { - function f() { } + export function f() { } } diff --git a/tests/baselines/reference/moduleElementsInWrongContext.js b/tests/baselines/reference/moduleElementsInWrongContext.js index cd440bd6017..b8c7ae25730 100644 --- a/tests/baselines/reference/moduleElementsInWrongContext.js +++ b/tests/baselines/reference/moduleElementsInWrongContext.js @@ -45,7 +45,7 @@ return C; }()); export default C; - function bee() { } + export function bee() { } import I2 = require("foo"); import * as Foo from "ambient"; import bar from "ambient"; diff --git a/tests/baselines/reference/moduleElementsInWrongContext2.js b/tests/baselines/reference/moduleElementsInWrongContext2.js index cc4aca0dec0..8582213a6ae 100644 --- a/tests/baselines/reference/moduleElementsInWrongContext2.js +++ b/tests/baselines/reference/moduleElementsInWrongContext2.js @@ -45,7 +45,7 @@ function blah() { return C; }()); export default C; - function bee() { } + export function bee() { } import I2 = require("foo"); import * as Foo from "ambient"; import bar from "ambient"; diff --git a/tests/baselines/reference/parserModifierOnStatementInBlock3.js b/tests/baselines/reference/parserModifierOnStatementInBlock3.js index 2815af43708..9f2cdf6850f 100644 --- a/tests/baselines/reference/parserModifierOnStatementInBlock3.js +++ b/tests/baselines/reference/parserModifierOnStatementInBlock3.js @@ -8,7 +8,7 @@ export function foo() { //// [parserModifierOnStatementInBlock3.js] "use strict"; function foo() { - function bar() { + export function bar() { } } exports.foo = foo; diff --git a/tests/baselines/reference/parserModifierOnStatementInBlock4.js b/tests/baselines/reference/parserModifierOnStatementInBlock4.js index 34b390468eb..ba2d256aeb9 100644 --- a/tests/baselines/reference/parserModifierOnStatementInBlock4.js +++ b/tests/baselines/reference/parserModifierOnStatementInBlock4.js @@ -7,6 +7,6 @@ //// [parserModifierOnStatementInBlock4.js] { - function bar() { + export function bar() { } } From 018bc741adcfc183ed0ab1be468d8aac66b97944 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Sat, 24 Sep 2016 16:21:26 -0700 Subject: [PATCH 3/3] Fix missing visit of expression in for..of --- src/compiler/transformers/es6.ts | 4 +-- .../reference/forOfTransformsExpression.js | 13 +++++++ .../forOfTransformsExpression.symbols | 25 +++++++++++++ .../reference/forOfTransformsExpression.types | 35 +++++++++++++++++++ .../compiler/forOfTransformsExpression.ts | 5 +++ 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/forOfTransformsExpression.js create mode 100644 tests/baselines/reference/forOfTransformsExpression.symbols create mode 100644 tests/baselines/reference/forOfTransformsExpression.types create mode 100644 tests/cases/compiler/forOfTransformsExpression.ts diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index 51a6a088fb7..41629fbf6d4 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -1765,7 +1765,7 @@ namespace ts { // Note also that because an extra statement is needed to assign to the LHS, // for-of bodies are always emitted as blocks. - const expression = node.expression; + const expression = visitNode(node.expression, visitor, isExpression); const initializer = node.initializer; const statements: Statement[] = []; @@ -2014,7 +2014,7 @@ namespace ts { case SyntaxKind.ForOfStatement: const initializer = (node).initializer; if (initializer && initializer.kind === SyntaxKind.VariableDeclarationList) { - loopInitializer = (node).initializer; + loopInitializer = initializer; } break; } diff --git a/tests/baselines/reference/forOfTransformsExpression.js b/tests/baselines/reference/forOfTransformsExpression.js new file mode 100644 index 00000000000..4b953b5cba1 --- /dev/null +++ b/tests/baselines/reference/forOfTransformsExpression.js @@ -0,0 +1,13 @@ +//// [forOfTransformsExpression.ts] +// https://github.com/Microsoft/TypeScript/issues/11024 +let items = [{ name: "A" }, { name: "C" }, { name: "B" }]; +for (var item of items.sort((a, b) => a.name.localeCompare(b.name))) { + +} + +//// [forOfTransformsExpression.js] +// https://github.com/Microsoft/TypeScript/issues/11024 +var items = [{ name: "A" }, { name: "C" }, { name: "B" }]; +for (var _i = 0, _a = items.sort(function (a, b) { return a.name.localeCompare(b.name); }); _i < _a.length; _i++) { + var item = _a[_i]; +} diff --git a/tests/baselines/reference/forOfTransformsExpression.symbols b/tests/baselines/reference/forOfTransformsExpression.symbols new file mode 100644 index 00000000000..9348f587084 --- /dev/null +++ b/tests/baselines/reference/forOfTransformsExpression.symbols @@ -0,0 +1,25 @@ +=== tests/cases/compiler/forOfTransformsExpression.ts === +// https://github.com/Microsoft/TypeScript/issues/11024 +let items = [{ name: "A" }, { name: "C" }, { name: "B" }]; +>items : Symbol(items, Decl(forOfTransformsExpression.ts, 1, 3)) +>name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 14)) +>name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 29)) +>name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 44)) + +for (var item of items.sort((a, b) => a.name.localeCompare(b.name))) { +>item : Symbol(item, Decl(forOfTransformsExpression.ts, 2, 8)) +>items.sort : Symbol(Array.sort, Decl(lib.d.ts, --, --)) +>items : Symbol(items, Decl(forOfTransformsExpression.ts, 1, 3)) +>sort : Symbol(Array.sort, Decl(lib.d.ts, --, --)) +>a : Symbol(a, Decl(forOfTransformsExpression.ts, 2, 29)) +>b : Symbol(b, Decl(forOfTransformsExpression.ts, 2, 31)) +>a.name.localeCompare : Symbol(String.localeCompare, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>a.name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 14)) +>a : Symbol(a, Decl(forOfTransformsExpression.ts, 2, 29)) +>name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 14)) +>localeCompare : Symbol(String.localeCompare, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>b.name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 14)) +>b : Symbol(b, Decl(forOfTransformsExpression.ts, 2, 31)) +>name : Symbol(name, Decl(forOfTransformsExpression.ts, 1, 14)) + +} diff --git a/tests/baselines/reference/forOfTransformsExpression.types b/tests/baselines/reference/forOfTransformsExpression.types new file mode 100644 index 00000000000..212cb202aab --- /dev/null +++ b/tests/baselines/reference/forOfTransformsExpression.types @@ -0,0 +1,35 @@ +=== tests/cases/compiler/forOfTransformsExpression.ts === +// https://github.com/Microsoft/TypeScript/issues/11024 +let items = [{ name: "A" }, { name: "C" }, { name: "B" }]; +>items : { name: string; }[] +>[{ name: "A" }, { name: "C" }, { name: "B" }] : { name: string; }[] +>{ name: "A" } : { name: string; } +>name : string +>"A" : "A" +>{ name: "C" } : { name: string; } +>name : string +>"C" : "C" +>{ name: "B" } : { name: string; } +>name : string +>"B" : "B" + +for (var item of items.sort((a, b) => a.name.localeCompare(b.name))) { +>item : { name: string; } +>items.sort((a, b) => a.name.localeCompare(b.name)) : { name: string; }[] +>items.sort : (compareFn?: (a: { name: string; }, b: { name: string; }) => number) => { name: string; }[] +>items : { name: string; }[] +>sort : (compareFn?: (a: { name: string; }, b: { name: string; }) => number) => { name: string; }[] +>(a, b) => a.name.localeCompare(b.name) : (a: { name: string; }, b: { name: string; }) => number +>a : { name: string; } +>b : { name: string; } +>a.name.localeCompare(b.name) : number +>a.name.localeCompare : { (that: string): number; (that: string, locales?: string | string[], options?: Intl.CollatorOptions): number; } +>a.name : string +>a : { name: string; } +>name : string +>localeCompare : { (that: string): number; (that: string, locales?: string | string[], options?: Intl.CollatorOptions): number; } +>b.name : string +>b : { name: string; } +>name : string + +} diff --git a/tests/cases/compiler/forOfTransformsExpression.ts b/tests/cases/compiler/forOfTransformsExpression.ts new file mode 100644 index 00000000000..e91bec012b1 --- /dev/null +++ b/tests/cases/compiler/forOfTransformsExpression.ts @@ -0,0 +1,5 @@ +// https://github.com/Microsoft/TypeScript/issues/11024 +let items = [{ name: "A" }, { name: "C" }, { name: "B" }]; +for (var item of items.sort((a, b) => a.name.localeCompare(b.name))) { + +} \ No newline at end of file