From 84ebbf774009ab3658b8b5760673b154d3d7413b Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 21 May 2015 21:59:29 +0200 Subject: [PATCH 01/12] Implement let in loop in binder & checker --- src/compiler/binder.ts | 22 ++++++++++++++++++---- src/compiler/checker.ts | 1 + src/compiler/types.ts | 3 +++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b66497fc40d59..fc42f911aa5be 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -62,6 +62,7 @@ module ts { let parent: Node; let container: Node; let blockScopeContainer: Node; + let iteration: IterationStatement; let lastContainer: Node; let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); @@ -227,7 +228,7 @@ module ts { // All container nodes are kept on a linked list in declaration order. This list is used by the getLocalNameOfContainer function // in the type checker to validate that the local name used for a container is unique. - function bindChildren(node: Node, symbolKind: SymbolFlags, isBlockScopeContainer: boolean) { + function bindChildren(node: Node, symbolKind: SymbolFlags, isBlockScopeContainer: boolean, isIteration: boolean = false) { if (symbolKind & SymbolFlags.HasLocals) { node.locals = {}; } @@ -235,6 +236,7 @@ module ts { let saveParent = parent; let saveContainer = container; let savedBlockScopeContainer = blockScopeContainer; + let savedIteration = iteration; parent = node; if (symbolKind & SymbolFlags.IsContainer) { container = node; @@ -251,11 +253,17 @@ module ts { // - node is a source file setBlockScopeContainer(node, /*cleanLocals*/ (symbolKind & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile); } + + if (isIteration) { + iteration = node; + iteration.iterationScopedDeclarations = []; + } forEachChild(node, bind); container = saveContainer; parent = saveParent; blockScopeContainer = savedBlockScopeContainer; + iteration = savedIteration; } function addToContainerChain(node: Node) { @@ -266,7 +274,7 @@ module ts { lastContainer = node; } - function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { + function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean, isIteration: boolean = false) { switch (container.kind) { case SyntaxKind.ModuleDeclaration: declareModuleMember(node, symbolKind, symbolExcludes); @@ -306,7 +314,7 @@ module ts { declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); break; } - bindChildren(node, symbolKind, isBlockScopeContainer); + bindChildren(node, symbolKind, isBlockScopeContainer, isIteration); } function isAmbientContext(node: Node): boolean { @@ -411,6 +419,9 @@ module ts { } declareSymbol(blockScopeContainer.locals, undefined, node, symbolKind, symbolExcludes); } + if (iteration) { + iteration.iterationScopedDeclarations.push(node); + } bindChildren(node, symbolKind, /*isBlockScopeContainer*/ false); } @@ -584,10 +595,13 @@ module ts { // 'let x' will be placed into the function locals and 'let x' - into the locals of the block bindChildren(node, 0, /*isBlockScopeContainer*/ !isFunctionLike(node.parent)); break; - case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: + case SyntaxKind.WhileStatement: + bindChildren(node, 0, /*isBlockScopeContainer*/ true, /*isIteration*/ true); + break; + case SyntaxKind.CatchClause: case SyntaxKind.CaseBlock: bindChildren(node, 0, /*isBlockScopeContainer*/ true); break; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9bd0d6475fe75..67d7cd8016970 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5538,6 +5538,7 @@ module ts { grammarErrorOnFirstToken(current, Diagnostics.Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher, declarationNameToString(node)); } // mark value declaration so during emit they can have a special handling + symbol.valueDeclaration.blockScopedBindingInLoop = true; getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; break; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 86e680ca8b047..bd75e2b8cfd55 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -407,6 +407,7 @@ module ts { export interface Declaration extends Node { _declarationBrand: any; name?: DeclarationName; + blockScopedBindingInLoop?: boolean; } export interface ComputedPropertyName extends Node { @@ -784,6 +785,8 @@ module ts { export interface IterationStatement extends Statement { statement: Statement; + // Block scoped declarations that are declared in this loop, but not in a subloop. + iterationScopedDeclarations: Declaration[]; } export interface DoStatement extends IterationStatement { From 3de966f6a3f83aa063ad627a788fa5b9536f023e Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 21 May 2015 21:59:44 +0200 Subject: [PATCH 02/12] Add basic emit for let in loops --- src/compiler/emitter.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d8d4bf73ed6f4..e8334a3de2d52 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2217,6 +2217,43 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { decreaseIndent(); } } + function emitDownlevelIterationEmbeddedStatement(node: Node) { + write(" (function() "); + if (node.kind === SyntaxKind.Block) { + emit(node); + } + else { + write("{"); + increaseIndent(); + writeLine(); + emit(node); + decreaseIndent(); + writeLine(); + write("}"); + } + } + function emitIterationEmbeddedStatement(node: IterationStatement) { + if (languageVersion >= ScriptTarget.ES6) { + emitEmbeddedStatement(node.statement); + return; + } + + let needsDownlevelEmit = false; + + for (const declaration of node.iterationScopedDeclarations) { + if (declaration.blockScopedBindingInLoop) { + needsDownlevelEmit = true; + break; + } + } + + if (!needsDownlevelEmit) { + emitEmbeddedStatement(node.statement); + } + else { + emitDownlevelIterationEmbeddedStatement(node.statement); + } + } function emitExpressionStatement(node: ExpressionStatement) { emitParenthesizedIf(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction); From b9b8bcc8d6dfef037daa1c61bc8f40bd4f048513 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 21 May 2015 22:13:00 +0200 Subject: [PATCH 03/12] Also treat DoStatement as iteration --- src/compiler/binder.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index fc42f911aa5be..d2ac3d2afd152 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -599,6 +599,7 @@ module ts { case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: bindChildren(node, 0, /*isBlockScopeContainer*/ true, /*isIteration*/ true); break; case SyntaxKind.CatchClause: From 609a69209138e82ff5abad71383a4c1a7ad7e8dd Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 21 May 2015 22:15:18 +0200 Subject: [PATCH 04/12] Call different emit method for iteration children --- src/compiler/emitter.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 73f436b44b87a..5a096b055c740 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2282,7 +2282,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { function emitDoStatement(node: DoStatement) { write("do"); - emitEmbeddedStatement(node.statement); + emitIterationEmbeddedStatement(node); if (node.statement.kind === SyntaxKind.Block) { write(" "); } @@ -2298,7 +2298,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write("while ("); emit(node.expression); write(")"); - emitEmbeddedStatement(node.statement); + emitIterationEmbeddedStatement(node); } /** @@ -2385,7 +2385,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write(";"); emitOptional(" ", node.incrementor); write(")"); - emitEmbeddedStatement(node.statement); + emitIterationEmbeddedStatement(node); } function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) { @@ -2415,7 +2415,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } emit(node.expression); emitToken(SyntaxKind.CloseParenToken, node.expression.end); - emitEmbeddedStatement(node.statement); + emitIterationEmbeddedStatement(node); } function emitDownLevelForOfStatement(node: ForOfStatement) { From cf4496325174d524c99f4aba3b4a1a2a484cdf43 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Thu, 21 May 2015 22:16:01 +0200 Subject: [PATCH 05/12] Fix downlevel loop as function emit --- src/compiler/emitter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5a096b055c740..f557a1bd2156d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2231,6 +2231,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { writeLine(); write("}"); } + write("})();"); } function emitIterationEmbeddedStatement(node: IterationStatement) { if (languageVersion >= ScriptTarget.ES6) { From 86b97a8a6d7a5bd981e40b69f49948c3f147a74d Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 09:57:16 +0200 Subject: [PATCH 06/12] Initialize function before the loop instead of creating a new function for every invocation. --- src/compiler/emitter.ts | 136 +++++++++++++++++++++++++++------------- 1 file changed, 93 insertions(+), 43 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f557a1bd2156d..31013fa0170a5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2205,54 +2205,81 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { scopeEmitEnd(); } - function emitEmbeddedStatement(node: Node) { + function emitEmbeddedStatement(node: Node, block?: boolean) { if (node.kind === SyntaxKind.Block) { write(" "); emit(node); } else { increaseIndent(); + if (block) write("{"); writeLine(); emit(node); decreaseIndent(); + if (block) { + writeLine(); + write("}"); + } } } - function emitDownlevelIterationEmbeddedStatement(node: Node) { - write(" (function() "); - if (node.kind === SyntaxKind.Block) { - emit(node); - } - else { - write("{"); - increaseIndent(); - writeLine(); - emit(node); - decreaseIndent(); - writeLine(); - write("}"); - } - write("})();"); - } - function emitIterationEmbeddedStatement(node: IterationStatement) { - if (languageVersion >= ScriptTarget.ES6) { - emitEmbeddedStatement(node.statement); - return; - } - + + function emitIteration(node: T, headDeclarations: Declaration[], emitHead: (node: T) => void, emitBody?: (node: T) => void, emitFooter?: (node: T) => void) { let needsDownlevelEmit = false; + const closureVariables: Identifier[] = []; - for (const declaration of node.iterationScopedDeclarations) { - if (declaration.blockScopedBindingInLoop) { - needsDownlevelEmit = true; - break; + if (languageVersion < ScriptTarget.ES6) { + for (const declaration of node.iterationScopedDeclarations) { + if (declaration.blockScopedBindingInLoop) { + needsDownlevelEmit = true; + if (headDeclarations.indexOf(declaration) !== -1) { + closureVariables.push( declaration.name); + } + } } } - if (!needsDownlevelEmit) { - emitEmbeddedStatement(node.statement); - } - else { - emitDownlevelIterationEmbeddedStatement(node.statement); + if (needsDownlevelEmit) { + let tempVariable = createTempVariable(TempFlags.Auto); + write("var "); + emit(tempVariable); + write(" = "); + write("function("); + emitList(closureVariables, 0, closureVariables.length, false, false); + write(") "); + + if (emitBody) { + emitBody(node); + } else { + emitEmbeddedStatement(node.statement, true); + } + + write(";"); + writeLine(); + + emitHead(node); + write(" "); + emit(tempVariable); + write("("); + emitList(closureVariables, 0, closureVariables.length, false, false); + write(");"); + + if (emitFooter) { + writeLine(); + emitFooter(node); + } + } else { + emitHead(node); + + if (emitBody) { + emitBody(node); + } else { + emitEmbeddedStatement(node.statement); + } + + if (emitFooter) { + emitFooter(node); + } + return; } } @@ -2281,9 +2308,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } } - function emitDoStatement(node: DoStatement) { + function emitDoStatementHead(node: DoStatement) { write("do"); - emitIterationEmbeddedStatement(node); + } + function emitDoStatementFooter(node: DoStatement) { if (node.statement.kind === SyntaxKind.Block) { write(" "); } @@ -2294,12 +2322,17 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { emit(node.expression); write(");"); } + function emitDoStatement(node: DoStatement) { + emitIteration(node, [], emitDoStatementHead, undefined, emitDoStatementFooter) + } - function emitWhileStatement(node: WhileStatement) { + function emitWhileStatementHead(node: WhileStatement) { write("while ("); emit(node.expression); write(")"); - emitIterationEmbeddedStatement(node); + } + function emitWhileStatement(node: WhileStatement) { + emitIteration(node, [], emitWhileStatementHead); } /** @@ -2364,7 +2397,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { return started; } - function emitForStatement(node: ForStatement) { + function emitForStatementHead(node: ForStatement) { + let variables: Declaration[] = []; let endPos = emitToken(SyntaxKind.ForKeyword, node.pos); write(" "); endPos = emitToken(SyntaxKind.OpenParenToken, endPos); @@ -2377,6 +2411,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { else { emitVariableDeclarationListSkippingUninitializedEntries(variableDeclarationList); } + variables = variableDeclarationList.declarations; } else if (node.initializer) { emit(node.initializer); @@ -2386,14 +2421,17 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write(";"); emitOptional(" ", node.incrementor); write(")"); - emitIterationEmbeddedStatement(node); } - - function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) { - if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.ForOfStatement) { - return emitDownLevelForOfStatement(node); + function emitForStatement(node: ForStatement) { + let variables: Declaration[] = []; + if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) { + variables = (node.initializer).declarations; } + + emitIteration(node, variables, emitForStatementHead); + } + function emitForInOrForOfStatementHead(node: ForInStatement | ForOfStatement) { let endPos = emitToken(SyntaxKind.ForKeyword, node.pos); write(" "); endPos = emitToken(SyntaxKind.OpenParenToken, endPos); @@ -2416,7 +2454,19 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } emit(node.expression); emitToken(SyntaxKind.CloseParenToken, node.expression.end); - emitIterationEmbeddedStatement(node); + } + function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) { + if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.ForOfStatement) { + return emitDownLevelForOfStatement(node); + } + + let variables: Declaration[] = []; + + if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { + variables = (node.initializer).declarations; + } + + emitIteration(node, variables, emitForInOrForOfStatementHead); } function emitDownLevelForOfStatement(node: ForOfStatement) { From eadb8a474b6d4eefd268ccea1716d4be741b8556 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 09:59:50 +0200 Subject: [PATCH 07/12] Only create new scope when inside function --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 67d7cd8016970..29037c018af90 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5535,10 +5535,10 @@ module ts { while (current && !nodeStartsNewLexicalEnvironment(current)) { if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { if (inFunction) { + symbol.valueDeclaration.blockScopedBindingInLoop = true; grammarErrorOnFirstToken(current, Diagnostics.Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher, declarationNameToString(node)); } // mark value declaration so during emit they can have a special handling - symbol.valueDeclaration.blockScopedBindingInLoop = true; getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; break; } From 60e15ed202cb2afc59024d878d8069273f039721 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 10:00:59 +0200 Subject: [PATCH 08/12] Remove error message --- src/compiler/checker.ts | 1 - src/compiler/diagnosticInformationMap.generated.ts | 1 - src/compiler/diagnosticMessages.json | 4 ---- 3 files changed, 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 29037c018af90..5d7c09e294774 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5536,7 +5536,6 @@ module ts { if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { if (inFunction) { symbol.valueDeclaration.blockScopedBindingInLoop = true; - grammarErrorOnFirstToken(current, Diagnostics.Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher, declarationNameToString(node)); } // mark value declaration so during emit they can have a special handling getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 2a8527168464a..5af2bbbab5f22 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -435,7 +435,6 @@ module ts { Parameter_0_of_exported_function_has_or_is_using_private_name_1: { code: 4078, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using private name '{1}'." }, Exported_type_alias_0_has_or_is_using_private_name_1: { code: 4081, category: DiagnosticCategory.Error, key: "Exported type alias '{0}' has or is using private name '{1}'." }, Default_export_of_the_module_has_or_is_using_private_name_0: { code: 4082, category: DiagnosticCategory.Error, key: "Default export of the module has or is using private name '{0}'." }, - Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher: { code: 4091, category: DiagnosticCategory.Error, key: "Loop contains block-scoped variable '{0}' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher." }, The_current_host_does_not_support_the_0_option: { code: 5001, category: DiagnosticCategory.Error, key: "The current host does not support the '{0}' option." }, Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." }, Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cf1645b140724..abff7b7256e79 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1729,10 +1729,6 @@ "category": "Error", "code": 4082 }, - "Loop contains block-scoped variable '{0}' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher.": { - "category": "Error", - "code": 4091 - }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 From 6ab0eb25ddae9c7f5501c88bd56da49783b6bd5f Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 11:58:54 +0200 Subject: [PATCH 09/12] Fix formatting `do ... while` statement --- src/compiler/emitter.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 31013fa0170a5..af5cf92a1ac18 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2223,7 +2223,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } } - function emitIteration(node: T, headDeclarations: Declaration[], emitHead: (node: T) => void, emitBody?: (node: T) => void, emitFooter?: (node: T) => void) { + function emitIteration(node: T, headDeclarations: Declaration[], emitHead: (node: T) => void, emitBody?: (node: T) => void, emitFooter?: (node: T, downlevel: boolean) => void) { let needsDownlevelEmit = false; const closureVariables: Identifier[] = []; @@ -2264,8 +2264,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write(");"); if (emitFooter) { - writeLine(); - emitFooter(node); + emitFooter(node, true); } } else { emitHead(node); @@ -2277,7 +2276,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } if (emitFooter) { - emitFooter(node); + emitFooter(node, false); } return; } @@ -2311,8 +2310,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { function emitDoStatementHead(node: DoStatement) { write("do"); } - function emitDoStatementFooter(node: DoStatement) { - if (node.statement.kind === SyntaxKind.Block) { + function emitDoStatementFooter(node: DoStatement, downlevel: boolean) { + if (node.statement.kind === SyntaxKind.Block || downlevel) { write(" "); } else { From 7cd9bef1f9d30da751ba436434c4966ea9bb0146 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 16:01:34 +0200 Subject: [PATCH 10/12] Implement for-of loops --- src/compiler/emitter.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index af5cf92a1ac18..63404a3b76702 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2223,7 +2223,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } } - function emitIteration(node: T, headDeclarations: Declaration[], emitHead: (node: T) => void, emitBody?: (node: T) => void, emitFooter?: (node: T, downlevel: boolean) => void) { + function emitIteration(node: T, headDeclarations: Declaration[], emitHead: (node: T) => void, emitBody?: (node: T, downlevel: boolean) => void, emitFooter?: (node: T, downlevel: boolean) => void) { let needsDownlevelEmit = false; const closureVariables: Identifier[] = []; @@ -2248,7 +2248,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write(") "); if (emitBody) { - emitBody(node); + emitBody(node, true); } else { emitEmbeddedStatement(node.statement, true); } @@ -2270,7 +2270,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { emitHead(node); if (emitBody) { - emitBody(node); + emitBody(node, false); } else { emitEmbeddedStatement(node.statement); } @@ -2468,7 +2468,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { emitIteration(node, variables, emitForInOrForOfStatementHead); } - function emitDownLevelForOfStatement(node: ForOfStatement) { + function emitDownLevelForOfStatementHead(node: ForOfStatement) { // The following ES6 code: // // for (let v of expr) { } @@ -2597,7 +2597,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } emitEnd(node.initializer); write(";"); - + } + + function emitDownLevelForOfStatementBody(node: ForOfStatement, isDownLevelLoopFunction: boolean) { + if (isDownLevelLoopFunction) { + emitEmbeddedStatement(node.statement, true); + return; + } + if (node.statement.kind === SyntaxKind.Block) { emitLines((node.statement).statements); } @@ -2605,11 +2612,21 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { writeLine(); emit(node.statement); } - + } + + function emitDownLevelForOfStatementFooter(node: ForOfStatement) { writeLine(); decreaseIndent(); write("}"); } + + function emitDownLevelForOfStatement(node: ForOfStatement) { + let variables: Declaration[] = []; + if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { + variables = ( node.initializer).declarations; + } + emitIteration(node, variables, emitDownLevelForOfStatementHead, emitDownLevelForOfStatementBody, emitDownLevelForOfStatementFooter); + } function emitBreakOrContinueStatement(node: BreakOrContinueStatement) { emitToken(node.kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword, node.pos); From 335f8c48c2ab7c34aa0dcad67537d946fcff1101 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 16:21:34 +0200 Subject: [PATCH 11/12] Accept new baselines --- .../reference/downlevelLetConst18.errors.txt | 23 +----------- .../reference/downlevelLetConst18.js | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/tests/baselines/reference/downlevelLetConst18.errors.txt b/tests/baselines/reference/downlevelLetConst18.errors.txt index 8f30b0f802a60..066c75abeb11e 100644 --- a/tests/baselines/reference/downlevelLetConst18.errors.txt +++ b/tests/baselines/reference/downlevelLetConst18.errors.txt @@ -1,60 +1,39 @@ -tests/cases/compiler/downlevelLetConst18.ts(3,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. tests/cases/compiler/downlevelLetConst18.ts(4,14): error TS2393: Duplicate function implementation. -tests/cases/compiler/downlevelLetConst18.ts(7,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. tests/cases/compiler/downlevelLetConst18.ts(8,14): error TS2393: Duplicate function implementation. -tests/cases/compiler/downlevelLetConst18.ts(11,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. -tests/cases/compiler/downlevelLetConst18.ts(15,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. -tests/cases/compiler/downlevelLetConst18.ts(19,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. -tests/cases/compiler/downlevelLetConst18.ts(23,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. -tests/cases/compiler/downlevelLetConst18.ts(27,1): error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. -==== tests/cases/compiler/downlevelLetConst18.ts (9 errors) ==== +==== tests/cases/compiler/downlevelLetConst18.ts (2 errors) ==== 'use strict' for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. function foo() { x }; ~~~ !!! error TS2393: Duplicate function implementation. } for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. function foo() { x }; ~~~ !!! error TS2393: Duplicate function implementation. } for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. (() => { x })(); } for (const x = 1; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. (() => { x })(); } for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. ({ foo() { x }}) } for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. ({ get foo() { return x } }) } for (let x; ;) { - ~~~ -!!! error TS4091: Loop contains block-scoped variable 'x' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher. ({ set foo(v) { x } }) } \ No newline at end of file diff --git a/tests/baselines/reference/downlevelLetConst18.js b/tests/baselines/reference/downlevelLetConst18.js index 70b8cb9e34653..38d2ee8a00611 100644 --- a/tests/baselines/reference/downlevelLetConst18.js +++ b/tests/baselines/reference/downlevelLetConst18.js @@ -32,26 +32,33 @@ for (let x; ;) { //// [downlevelLetConst18.js] 'use strict'; -for (var x = void 0;;) { +var _a = function(x) { function foo() { x; } ; -} -for (var x = void 0;;) { +}; +for (var x = void 0;;) _a(x); +var _b = function(x) { function foo() { x; } ; -} -for (var x = void 0;;) { +}; +for (var x = void 0;;) _b(x); +var _c = function(x) { (function () { x; })(); -} -for (var x = 1;;) { +}; +for (var x = void 0;;) _c(x); +var _d = function(x) { (function () { x; })(); -} -for (var x = void 0;;) { +}; +for (var x = 1;;) _d(x); +var _e = function(x) { ({ foo: function () { x; } }); -} -for (var x = void 0;;) { +}; +for (var x = void 0;;) _e(x); +var _f = function(x) { ({ get foo() { return x; } }); -} -for (var x = void 0;;) { +}; +for (var x = void 0;;) _f(x); +var _g = function(x) { ({ set foo(v) { x; } }); -} +}; +for (var x = void 0;;) _g(x); From 9e4c23ada74d46c675e04c57093ce5b4c5d7bba8 Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 22 May 2015 16:38:39 +0200 Subject: [PATCH 12/12] Emit single space instead of double --- src/compiler/emitter.ts | 4 ++-- tests/baselines/reference/downlevelLetConst18.js | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 63404a3b76702..eff83758587e7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2212,7 +2212,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { } else { increaseIndent(); - if (block) write("{"); + if (block) write(" {"); writeLine(); emit(node); decreaseIndent(); @@ -2245,7 +2245,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) { write(" = "); write("function("); emitList(closureVariables, 0, closureVariables.length, false, false); - write(") "); + write(")"); if (emitBody) { emitBody(node, true); diff --git a/tests/baselines/reference/downlevelLetConst18.js b/tests/baselines/reference/downlevelLetConst18.js index 38d2ee8a00611..fec851a7e0a71 100644 --- a/tests/baselines/reference/downlevelLetConst18.js +++ b/tests/baselines/reference/downlevelLetConst18.js @@ -32,33 +32,33 @@ for (let x; ;) { //// [downlevelLetConst18.js] 'use strict'; -var _a = function(x) { +var _a = function(x) { function foo() { x; } ; }; for (var x = void 0;;) _a(x); -var _b = function(x) { +var _b = function(x) { function foo() { x; } ; }; for (var x = void 0;;) _b(x); -var _c = function(x) { +var _c = function(x) { (function () { x; })(); }; for (var x = void 0;;) _c(x); -var _d = function(x) { +var _d = function(x) { (function () { x; })(); }; for (var x = 1;;) _d(x); -var _e = function(x) { +var _e = function(x) { ({ foo: function () { x; } }); }; for (var x = void 0;;) _e(x); -var _f = function(x) { +var _f = function(x) { ({ get foo() { return x; } }); }; for (var x = void 0;;) _f(x); -var _g = function(x) { +var _g = function(x) { ({ set foo(v) { x; } }); }; for (var x = void 0;;) _g(x);