From 33dfe5068a2ee2f5880fd604fadb7d40a2597fd3 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Wed, 25 Feb 2015 17:44:09 -0800 Subject: [PATCH] do not emit default initializer for let\const in for-in\for-of statements --- src/compiler/checker.ts | 2 +- src/compiler/emitter.ts | 40 ++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 68e88437647..7918d393c0b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5119,9 +5119,9 @@ module ts { while (current && !isNameScopeBoundary(current)) { if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { if (inFunction) { - getNodeLinks(current).flags |= NodeCheckFlags.BlockScopedBindingInLoop; grammarErrorOnFirstToken(current, Diagnostics.Code_in_the_loop_captures_block_scoped_variable_0_in_closure_This_is_natively_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; break; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9bfc14caaa0..1cec38872af 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1702,6 +1702,7 @@ module ts { function makeUniqueName(location: Node, baseName: string): string { var name: string + // first try to check if base name can be used as is if (!isExistingName(location, baseName)) { name = baseName; } @@ -1721,12 +1722,9 @@ module ts { return currentScopeNames[name] = name; } - function isGeneratedName(name: string): boolean { - return currentScopeNames && hasProperty(currentScopeNames, name); - } - function isExistingName(location: Node, name: string) { - return !resolver.isUnknownIdentifier(location, name) || isGeneratedName(name); + return !resolver.isUnknownIdentifier(location, name) || + (currentScopeNames && hasProperty(currentScopeNames, name)); } function initializeEmitterWithSourceMaps() { @@ -3559,7 +3557,6 @@ module ts { result.expression = zero; return result; } - function emitExportMemberAssignments(name: Identifier) { if (exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { @@ -3802,18 +3799,24 @@ module ts { emitModuleMemberName(node); var initializer = node.initializer; - if (!initializer) { + if (!initializer && languageVersion < ScriptTarget.ES6) { + // downlevel emit for non-initialized let bindings defined in loops // for (...) { let x; } // should be // for (...) { var = void 0; } // this is necessary to preserve ES6 semantic in scenarios like // for (...) { let x; console.log(x); x = 1 } // assignment on one iteration should not affect other iterations - var initializer = - languageVersion < ScriptTarget.ES6 && + var isUninitializedLet = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.BlockScopedBindingInLoop) && - (getCombinedFlagsForIdentifier(node.name) & NodeFlags.Let) && - createVoidZero(); + (getCombinedFlagsForIdentifier(node.name) & NodeFlags.Let); + + // NOTE: default initialization should not be added to let bindings in for-in\for-of statements + if (isUninitializedLet && + node.parent.parent.kind !== SyntaxKind.ForInStatement && + node.parent.parent.kind !== SyntaxKind.ForOfStatement) { + initializer = createVoidZero(); + } } emitOptional(" = ", initializer); @@ -3834,20 +3837,20 @@ module ts { var current = node; while (current) { if (isAnyFunction(current)) { - return current.parent; + return current; } switch (current.kind) { case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.SwitchKeyword: - return current.parent; + return current; case SyntaxKind.Block: if (isAnyFunction(current.parent)) { - return current.parent.parent; + return current.parent; } else { - return current.parent; + return current; } case SyntaxKind.SourceFile: return current; @@ -3893,7 +3896,12 @@ module ts { return; } - var generatedName = makeUniqueName(getEnclosingBlockScopeContainer(node), (node).text); + var blockScopeContainer = getEnclosingBlockScopeContainer(node); + var parent = blockScopeContainer.kind === SyntaxKind.SourceFile + ? blockScopeContainer + : blockScopeContainer.parent; + + var generatedName = makeUniqueName(parent, (node).text); var variableId = resolver.getBlockScopedVariableId(node); if (!generatedBlockScopeNames) { generatedBlockScopeNames = [];