Merge pull request #2161 from Microsoft/letConstES5Minus

Downlevel emit for let\const
This commit is contained in:
Vladimir Matveev
2015-02-27 23:02:35 -08:00
118 changed files with 2958 additions and 326 deletions
+16 -2
View File
@@ -67,7 +67,8 @@ module ts {
if (!file.locals) {
file.locals = {};
container = blockScopeContainer = file;
container = file;
setBlockScopeContainer(file, /*cleanLocals*/ false);
bind(file);
file.symbolCount = symbolCount;
}
@@ -77,6 +78,13 @@ module ts {
return new Symbol(flags, name);
}
function setBlockScopeContainer(node: Node, cleanLocals: boolean) {
blockScopeContainer = node;
if (cleanLocals) {
blockScopeContainer.locals = undefined;
}
}
function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolKind: SymbolFlags) {
symbol.flags |= symbolKind;
if (!symbol.declarations) symbol.declarations = [];
@@ -236,7 +244,13 @@ module ts {
}
if (isBlockScopeContainer) {
blockScopeContainer = node;
// in incremental scenarios we might reuse nodes that already have locals being allocated
// during the bind step these locals should be dropped to prevent using stale data.
// locals should always be dropped unless they were previously initialized by the binder
// these cases are:
// - node has locals (symbolKind & HasLocals) !== 0
// - node is a source file
setBlockScopeContainer(node, /*cleanLocals*/ (symbolKind & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile);
}
forEachChild(node, bind);
+98 -29
View File
@@ -5075,10 +5075,63 @@ module ts {
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithCapturedThisVariable(node, node);
checkBlockScopedBindingCapturedInLoop(node, symbol);
return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node);
}
function isInsideFunction(node: Node, threshold: Node): boolean {
var current = node;
while (current && current !== threshold) {
if (isAnyFunction(current)) {
return true;
}
current = current.parent;
}
return false;
}
function checkBlockScopedBindingCapturedInLoop(node: Identifier, symbol: Symbol): void {
if (languageVersion >= ScriptTarget.ES6 ||
(symbol.flags & SymbolFlags.BlockScopedVariable) === 0 ||
symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) {
return;
}
// - check if binding is used in some function
// (stop the walk when reaching container of binding declaration)
// - if first check succeeded - check if variable is declared inside the loop
// nesting structure:
// (variable declaration or binding element) -> variable declaration list -> container
var container: Node = symbol.valueDeclaration;
while (container.kind !== SyntaxKind.VariableDeclarationList) {
container = container.parent;
}
// get the parent of variable declaration list
container = container.parent;
if (container.kind === SyntaxKind.VariableStatement) {
// if parent is variable statement - get its parent
container = container.parent;
}
var inFunction = isInsideFunction(node.parent, container);
var current = container;
while (current && !nodeStartsNewLexicalEnvironment(current)) {
if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) {
if (inFunction) {
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(<VariableDeclaration>symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
break;
}
current = current.parent;
}
}
function captureLexicalThis(node: Node, container: Node): void {
var classNode = container.parent && container.parent.kind === SyntaxKind.ClassDeclaration ? container.parent : undefined;
getNodeLinks(node).flags |= NodeCheckFlags.LexicalThis;
@@ -10467,25 +10520,8 @@ module ts {
}
function makeUniqueName(baseName: string): string {
// First try '_name'
if (baseName.charCodeAt(0) !== CharacterCodes._) {
var baseName = "_" + baseName;
if (!isExistingName(baseName)) {
return generatedNames[baseName] = baseName;
}
}
// Find the first unique '_name_n', where n is a positive number
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
baseName += "_";
}
var i = 1;
while (true) {
name = baseName + i;
if (!isExistingName(name)) {
return generatedNames[name] = name;
}
i++;
}
var name = generateUniqueName(baseName, isExistingName);
return generatedNames[name] = name;
}
function assignGeneratedName(node: Node, name: string) {
@@ -10686,6 +10722,46 @@ module ts {
!hasProperty(getGeneratedNamesForSourceFile(getSourceFile(location)), name);
}
function getBlockScopedVariableId(n: Identifier): number {
Debug.assert(!nodeIsSynthesized(n));
// ignore name parts of property access expressions
if (n.parent.kind === SyntaxKind.PropertyAccessExpression &&
(<PropertyAccessExpression>n.parent).name === n) {
return undefined;
}
// ignore property names in object binding patterns
if (n.parent.kind === SyntaxKind.BindingElement &&
(<BindingElement>n.parent).propertyName === n) {
return undefined;
}
// for names in variable declarations and binding elements try to short circuit and fetch symbol from the node
var declarationSymbol: Symbol =
(n.parent.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>n.parent).name === n) ||
n.parent.kind === SyntaxKind.BindingElement
? getSymbolOfNode(n.parent)
: undefined;
var symbol = declarationSymbol ||
getNodeLinks(n).resolvedSymbol ||
resolveName(n, n.text, SymbolFlags.BlockScopedVariable | SymbolFlags.Import, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
var isLetOrConst =
symbol &&
(symbol.flags & SymbolFlags.BlockScopedVariable) &&
symbol.valueDeclaration.parent.kind !== SyntaxKind.CatchClause;
if (isLetOrConst) {
// side-effect of calling this method:
// assign id to symbol if it was not yet set
getSymbolLinks(symbol);
return symbol.id;
}
return undefined;
}
function createResolver(): EmitResolver {
return {
getGeneratedNameForNode,
@@ -10702,6 +10778,7 @@ module ts {
isEntityNameVisible,
getConstantValue,
isUnknownIdentifier,
getBlockScopedVariableId,
};
}
@@ -11443,7 +11520,8 @@ module ts {
if (isBindingPattern(node.name) && !isBindingPattern(node.parent)) {
return grammarErrorOnNode(node, Diagnostics.A_destructuring_declaration_must_have_an_initializer);
}
if (isConst(node)) {
// const declarations should not be initialized in for-in for-of statements
if (isConst(node) && node.parent.parent.kind !== SyntaxKind.ForInStatement && node.parent.parent.kind !== SyntaxKind.ForOfStatement) {
return grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized);
}
}
@@ -11485,15 +11563,6 @@ module ts {
if (!declarationList.declarations.length) {
return grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
if (languageVersion < ScriptTarget.ES6) {
if (isLet(declarationList)) {
return grammarErrorOnFirstToken(declarationList, Diagnostics.let_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
else if (isConst(declarationList)) {
return grammarErrorOnFirstToken(declarationList, Diagnostics.const_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
}
}
function allowLetAndConstDeclarations(parent: Node): boolean {
@@ -397,6 +397,7 @@ module ts {
Parameter_0_of_exported_function_has_or_is_using_name_1_from_private_module_2: { code: 4077, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'." },
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}'." },
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}" },
+4
View File
@@ -1581,6 +1581,10 @@
"category": "Error",
"code": 4081
},
"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
+231 -66
View File
@@ -28,9 +28,10 @@ module ts {
typeName?: DeclarationName;
}
interface SynthesizedNode extends Node {
leadingCommentRanges?: CommentRange[];
trailingCommentRanges?: CommentRange[];
// represents one LexicalEnvironment frame to store unique generated names
interface ScopeFrame {
names: Map<string>;
previous: ScopeFrame;
}
type GetSymbolAccessibilityDiagnostic = (symbolAccesibilityResult: SymbolAccessiblityResult) => SymbolAccessibilityDiagnostic;
@@ -369,7 +370,6 @@ module ts {
var enclosingDeclaration: Node;
var currentSourceFile: SourceFile;
var reportedDeclarationError = false;
var emitJsDocComments = compilerOptions.removeComments ? function (declaration: Node) { } : writeJsDocComments;
var emit = compilerOptions.stripInternal ? stripInternal : emitNode;
@@ -1523,10 +1523,6 @@ module ts {
return diagnostics;
}
interface SynthesizedNode extends Node {
startsOnNewLine: boolean;
}
// @internal
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult {
@@ -1578,6 +1574,11 @@ module ts {
var currentSourceFile: SourceFile;
var lastFrame: ScopeFrame;
var currentScopeNames: Map<string>;
var generatedBlockScopeNames: string[];
var extendsEmitted = false;
var tempCount = 0;
var tempVariables: Identifier[];
@@ -1652,6 +1653,50 @@ module ts {
writeEmittedFiles(writer.getText(), /*writeByteOrderMark*/ compilerOptions.emitBOM);
return;
// enters the new lexical environment
// return value should be passed to matching call to exitNameScope.
function enterNameScope(): boolean {
var names = currentScopeNames;
currentScopeNames = undefined;
if (names) {
lastFrame = { names, previous: lastFrame };
return true;
}
return false;
}
function exitNameScope(popFrame: boolean): void {
if (popFrame) {
currentScopeNames = lastFrame.names;
lastFrame = lastFrame.previous;
}
else {
currentScopeNames = undefined;
}
}
function generateUniqueNameForLocation(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;
}
else {
name = generateUniqueName(baseName, n => isExistingName(location, n));
}
if (!currentScopeNames) {
currentScopeNames = {};
}
return currentScopeNames[name] = name;
}
function isExistingName(location: Node, name: string) {
return !resolver.isUnknownIdentifier(location, name) ||
(currentScopeNames && hasProperty(currentScopeNames, name));
}
function initializeEmitterWithSourceMaps() {
var sourceMapDir: string; // The directory in which sourcemap will be
@@ -2018,14 +2063,14 @@ module ts {
function createTempVariable(location: Node, forLoopVariable?: boolean): Identifier {
var name = forLoopVariable ? "_i" : undefined;
while (true) {
if (name && resolver.isUnknownIdentifier(location, name)) {
if (name && !isExistingName(location, name)) {
break;
}
// _a .. _h, _j ... _z, _0, _1, ...
name = "_" + (tempCount < 25 ? String.fromCharCode(tempCount + (tempCount < 8 ? 0 : 1) + CharacterCodes.a) : tempCount - 25);
tempCount++;
}
var result = <Identifier>createNode(SyntaxKind.Identifier);
var result = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
result.text = name;
return result;
}
@@ -2478,7 +2523,20 @@ module ts {
}
}
function getBlockScopedVariableId(node: Identifier): number {
// return undefined for synthesized nodes
return !nodeIsSynthesized(node) && resolver.getBlockScopedVariableId(node);
}
function emitIdentifier(node: Identifier) {
var variableId = getBlockScopedVariableId(node);
if (variableId !== undefined && generatedBlockScopeNames) {
var text = generatedBlockScopeNames[variableId];
if (text) {
write(text);
return;
}
}
if (!node.parent) {
write(node.text);
}
@@ -2624,19 +2682,6 @@ module ts {
}
}
function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
var node = <SynthesizedNode>createNode(kind);
node.pos = -1;
node.end = -1;
node.startsOnNewLine = startsOnNewLine;
return node;
}
function isSynthesized(node: Node) {
return node.pos === -1 && node.end === -1;
}
function emitDownlevelObjectLiteralWithComputedProperties(node: ObjectLiteralExpression, firstComputedPropertyIndex: number): void {
var parenthesizedObjectLiteral = createDownlevelObjectLiteralWithComputedProperties(node, firstComputedPropertyIndex);
return emit(parenthesizedObjectLiteral);
@@ -3167,7 +3212,7 @@ module ts {
write(tokenToString(node.operatorToken.kind));
var shouldPlaceOnNewLine = !isSynthesized(node) && !nodeEndIsOnSameLineAsNodeStart(node.operatorToken, node.right);
var shouldPlaceOnNewLine = !nodeIsSynthesized(node) && !nodeEndIsOnSameLineAsNodeStart(node.operatorToken, node.right);
// Check if the right expression is on a different line versus the operator itself. If so,
// we'll emit newline.
@@ -3185,7 +3230,7 @@ module ts {
}
function synthesizedNodeStartsOnNewLine(node: Node) {
return isSynthesized(node) && (<SynthesizedNode>node).startsOnNewLine;
return nodeIsSynthesized(node) && (<SynthesizedNode>node).startsOnNewLine;
}
function emitConditionalExpression(node: ConditionalExpression) {
@@ -3287,6 +3332,32 @@ module ts {
emitEmbeddedStatement(node.statement);
}
function emitStartOfVariableDeclarationList(decl: Node, startPos?: number): void {
var tokenKind = SyntaxKind.VarKeyword;
if (decl && languageVersion >= ScriptTarget.ES6) {
if (isLet(decl)) {
tokenKind = SyntaxKind.LetKeyword;
}
else if (isConst(decl)) {
tokenKind = SyntaxKind.ConstKeyword;
}
}
if (startPos !== undefined) {
emitToken(tokenKind, startPos);
}
else {
switch (tokenKind) {
case SyntaxKind.VarKeyword:
return write("var ");
case SyntaxKind.LetKeyword:
return write("let ");
case SyntaxKind.ConstKeyword:
return write("const ");
}
}
}
function emitForStatement(node: ForStatement) {
var endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
write(" ");
@@ -3294,17 +3365,9 @@ module ts {
if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) {
var variableDeclarationList = <VariableDeclarationList>node.initializer;
var declarations = variableDeclarationList.declarations;
if (declarations[0] && isLet(declarations[0])) {
emitToken(SyntaxKind.LetKeyword, endPos);
}
else if (declarations[0] && isConst(declarations[0])) {
emitToken(SyntaxKind.ConstKeyword, endPos);
}
else {
emitToken(SyntaxKind.VarKeyword, endPos);
}
emitStartOfVariableDeclarationList(declarations[0], endPos);
write(" ");
emitCommaList(variableDeclarationList.declarations);
emitCommaList(declarations);
}
else if (node.initializer) {
emit(node.initializer);
@@ -3325,12 +3388,7 @@ module ts {
var variableDeclarationList = <VariableDeclarationList>node.initializer;
if (variableDeclarationList.declarations.length >= 1) {
var decl = variableDeclarationList.declarations[0];
if (isLet(decl)) {
emitToken(SyntaxKind.LetKeyword, endPos);
}
else {
emitToken(SyntaxKind.VarKeyword, endPos);
}
emitStartOfVariableDeclarationList(decl, endPos);
write(" ");
emit(decl);
}
@@ -3479,6 +3537,14 @@ module ts {
emitNode(node.name);
emitEnd(node.name);
}
function createVoidZero(): Expression {
var zero = <LiteralExpression>createSynthesizedNode(SyntaxKind.NumericLiteral);
zero.text = "0";
var result = <VoidExpression>createSynthesizedNode(SyntaxKind.VoidExpression);
result.expression = zero;
return result;
}
function emitExportMemberAssignments(name: Identifier) {
if (exportSpecifiers && hasProperty(exportSpecifiers, name.text)) {
@@ -3512,6 +3578,8 @@ module ts {
if (emitCount++) {
write(", ");
}
renameNonTopLevelLetAndConst(name);
if (name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement)) {
emitModuleMemberName(<Declaration>name.parent);
}
@@ -3534,24 +3602,16 @@ module ts {
return expr;
}
function createVoidZero(): Expression {
var zero = <LiteralExpression>createNode(SyntaxKind.NumericLiteral);
zero.text = "0";
var result = <VoidExpression>createNode(SyntaxKind.VoidExpression);
result.expression = zero;
return result;
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression {
// The value expression will be evaluated twice, so for anything but a simple identifier
// we need to generate a temporary variable
value = ensureIdentifier(value);
// Return the expression 'value === void 0 ? defaultValue : value'
var equals = <BinaryExpression>createNode(SyntaxKind.BinaryExpression);
var equals = <BinaryExpression>createSynthesizedNode(SyntaxKind.BinaryExpression);
equals.left = value;
equals.operatorToken = createNode(SyntaxKind.EqualsEqualsEqualsToken);
equals.operatorToken = createSynthesizedNode(SyntaxKind.EqualsEqualsEqualsToken);
equals.right = createVoidZero();
var cond = <ConditionalExpression>createNode(SyntaxKind.ConditionalExpression);
var cond = <ConditionalExpression>createSynthesizedNode(SyntaxKind.ConditionalExpression);
cond.condition = equals;
cond.whenTrue = defaultValue;
cond.whenFalse = value;
@@ -3559,7 +3619,7 @@ module ts {
}
function createNumericLiteral(value: number) {
var node = <LiteralExpression>createNode(SyntaxKind.NumericLiteral);
var node = <LiteralExpression>createSynthesizedNode(SyntaxKind.NumericLiteral);
node.text = "" + value;
return node;
}
@@ -3568,7 +3628,7 @@ module ts {
if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) {
return <LeftHandSideExpression>expr;
}
var node = <ParenthesizedExpression>createNode(SyntaxKind.ParenthesizedExpression);
var node = <ParenthesizedExpression>createSynthesizedNode(SyntaxKind.ParenthesizedExpression);
node.expression = expr;
return node;
}
@@ -3577,14 +3637,14 @@ module ts {
if (propName.kind !== SyntaxKind.Identifier) {
return createElementAccess(object, propName);
}
var node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression);
var node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
node.expression = parenthesizeForAccess(object);
node.name = propName;
return node;
}
function createElementAccess(object: Expression, index: Expression): Expression {
var node = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression);
var node = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression);
node.expression = parenthesizeForAccess(object);
node.argumentExpression = index;
return node;
@@ -3723,8 +3783,31 @@ module ts {
}
}
else {
var isLet = renameNonTopLevelLetAndConst(<Identifier>node.name);
emitModuleMemberName(node);
emitOptional(" = ", node.initializer);
var initializer = node.initializer;
if (!initializer && languageVersion < ScriptTarget.ES6) {
// downlevel emit for non-initialized let bindings defined in loops
// for (...) { let x; }
// should be
// for (...) { var <some-uniqie-name> = 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 isUninitializedLet =
(resolver.getNodeCheckFlags(node) & NodeCheckFlags.BlockScopedBindingInLoop) &&
(getCombinedFlagsForIdentifier(<Identifier>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);
}
}
@@ -3737,18 +3820,86 @@ module ts {
forEach((<BindingPattern>name).elements, emitExportVariableAssignments);
}
}
function getEnclosingBlockScopeContainer(node: Node): Node {
var current = node;
while (current) {
if (isAnyFunction(current)) {
return current;
}
switch (current.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.SwitchKeyword:
case SyntaxKind.CatchClause:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
return current;
case SyntaxKind.Block:
// function block is not considered block-scope container
// see comment in binder.ts: bind(...), case for SyntaxKind.Block
if (!isAnyFunction(current.parent)) {
return current;
}
}
current = current.parent;
}
}
function getCombinedFlagsForIdentifier(node: Identifier): NodeFlags {
if (!node.parent || (node.parent.kind !== SyntaxKind.VariableDeclaration && node.parent.kind !== SyntaxKind.BindingElement)) {
return 0;
}
return getCombinedNodeFlags(node.parent);
}
function renameNonTopLevelLetAndConst(node: Node): void {
// do not rename if
// - language version is ES6+
// - node is synthesized
// - node is not identifier (can happen when tree is malformed)
// - node is definitely not name of variable declaration.
// it still can be part of parameter declaration, this check will be done next
if (languageVersion >= ScriptTarget.ES6 ||
nodeIsSynthesized(node) ||
node.kind !== SyntaxKind.Identifier ||
(node.parent.kind !== SyntaxKind.VariableDeclaration && node.parent.kind !== SyntaxKind.BindingElement)) {
return;
}
var combinedFlags = getCombinedFlagsForIdentifier(<Identifier>node);
if (((combinedFlags & NodeFlags.BlockScoped) === 0) || combinedFlags & NodeFlags.Export) {
// do not rename exported or non-block scoped variables
return;
}
// here it is known that node is a block scoped variable
var list = getAncestor(node, SyntaxKind.VariableDeclarationList);
if (list.parent.kind === SyntaxKind.VariableStatement && list.parent.parent.kind === SyntaxKind.SourceFile) {
// do not rename variables that are defined on source file level
return;
}
var blockScopeContainer = getEnclosingBlockScopeContainer(node);
var parent = blockScopeContainer.kind === SyntaxKind.SourceFile
? blockScopeContainer
: blockScopeContainer.parent;
var generatedName = generateUniqueNameForLocation(parent, (<Identifier>node).text);
var variableId = resolver.getBlockScopedVariableId(<Identifier>node);
if (!generatedBlockScopeNames) {
generatedBlockScopeNames = [];
}
generatedBlockScopeNames[variableId] = generatedName;
}
function emitVariableStatement(node: VariableStatement) {
if (!(node.flags & NodeFlags.Export)) {
if (isLet(node.declarationList)) {
write("let ");
}
else if (isConst(node.declarationList)) {
write("const ");
}
else {
write("var ");
}
emitStartOfVariableDeclarationList(node.declarationList);
}
emitCommaList(node.declarationList.declarations);
write(";");
@@ -3925,6 +4076,8 @@ module ts {
tempVariables = undefined;
tempParameters = undefined;
var popFrame = enterNameScope()
// When targeting ES6, emit arrow function natively in ES6
if (shouldEmitAsArrowFunction(node)) {
emitSignatureParametersForArrow(node);
@@ -3956,6 +4109,8 @@ module ts {
write(";");
}
exitNameScope(popFrame);
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
@@ -4277,6 +4432,9 @@ module ts {
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
var popFrame = enterNameScope();
// Emit the constructor overload pinned comments
forEach(node.members, member => {
if (member.kind === SyntaxKind.Constructor && !(<ConstructorDeclaration>member).body) {
@@ -4337,6 +4495,9 @@ module ts {
if (ctor) {
emitTrailingComments(ctor);
}
exitNameScope(popFrame);
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
@@ -4469,7 +4630,11 @@ module ts {
var saveTempVariables = tempVariables;
tempCount = 0;
tempVariables = undefined;
var popFrame = enterNameScope();
emit(node.body);
exitNameScope(popFrame);
tempCount = saveTempCount;
tempVariables = saveTempVariables;
}
+2
View File
@@ -1204,6 +1204,7 @@ module ts {
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
isUnknownIdentifier(location: Node, name: string): boolean;
getBlockScopedVariableId(node: Identifier): number;
}
export const enum SymbolFlags {
@@ -1329,6 +1330,7 @@ module ts {
// Values for enum members have been computed, and any errors have been reported for them.
EnumValuesComputed = 0x00000080,
BlockScopedBindingInLoop = 0x00000100,
}
export interface NodeLinks {
+44 -1
View File
@@ -7,6 +7,12 @@ module ts {
isNoDefaultLib?: boolean
}
export interface SynthesizedNode extends Node {
leadingCommentRanges?: CommentRange[];
trailingCommentRanges?: CommentRange[];
startsOnNewLine: boolean;
}
export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration {
var declarations = symbol.declarations;
for (var i = 0; i < declarations.length; i++) {
@@ -1133,7 +1139,44 @@ module ts {
return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength: */newEndN - oldStartN);
}
// @internal
export function nodeStartsNewLexicalEnvironment(n: Node): boolean {
return isAnyFunction(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile;
}
export function nodeIsSynthesized(node: Node): boolean {
return node.pos === -1 && node.end === -1;
}
export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
var node = <SynthesizedNode>createNode(kind);
node.pos = -1;
node.end = -1;
node.startsOnNewLine = startsOnNewLine;
return node;
}
export function generateUniqueName(baseName: string, isExistingName: (name: string) => boolean): string {
// First try '_name'
if (baseName.charCodeAt(0) !== CharacterCodes._) {
var baseName = "_" + baseName;
if (!isExistingName(baseName)) {
return baseName;
}
}
// Find the first unique '_name_n', where n is a positive number
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
baseName += "_";
}
var i = 1;
while (true) {
var name = baseName + i;
if (!isExistingName(name)) {
return name;
}
i++;
}
}
export function createDiagnosticCollection(): DiagnosticCollection {
var nonFileDiagnostics: Diagnostic[] = [];
var fileDiagnostics: Map<Diagnostic[]> = {};