diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 8f388a01520..eae769fd562 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -185,24 +185,38 @@ namespace ts { // Identifiers - export function createIdentifier(text: string): Identifier { - const node = createNode(SyntaxKind.Identifier); + export function createIdentifier(text: string, location?: TextRange): Identifier { + const node = createNode(SyntaxKind.Identifier, location); node.text = text; return node; } - export function createTempVariable(): Identifier { - const name = createNode(SyntaxKind.Identifier); - name.text = undefined; - name.tempKind = TempVariableKind.Auto; + export function createTempVariable(location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Auto; getNodeId(name); return name; } - export function createLoopVariable(): Identifier { - const name = createNode(SyntaxKind.Identifier); - name.text = undefined; - name.tempKind = TempVariableKind.Loop; + export function createLoopVariable(location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Loop; + getNodeId(name); + return name; + } + + export function createUniqueName(text: string, location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.text = text; + name.autoGenerateKind = GeneratedIdentifierKind.Unique; + getNodeId(name); + return name; + } + + export function createGeneratedNameForNode(node: Node, location?: TextRange): Identifier { + const name = createNode(SyntaxKind.Identifier, location); + name.autoGenerateKind = GeneratedIdentifierKind.Node; + name.original = node; getNodeId(name); return name; } diff --git a/src/compiler/printer.ts b/src/compiler/printer.ts index a1d284dd42b..e44e88b4612 100644 --- a/src/compiler/printer.ts +++ b/src/compiler/printer.ts @@ -121,7 +121,6 @@ const _super = (function (geti, seti) { const writer = createTextWriter(newLine); const { write, - writeTextOfNode, writeLine, increaseIndent, decreaseIndent @@ -154,11 +153,12 @@ const _super = (function (geti, seti) { let identifierSubstitution: (node: Identifier) => Identifier; let onBeforeEmitNode: (node: Node) => void; let onAfterEmitNode: (node: Node) => void; - let isUniqueName: (name: string) => boolean; - let temporaryVariables: string[] = []; + let nodeToGeneratedName: string[]; + let generatedNameSet: Map; let tempFlags: TempFlags; let currentSourceFile: SourceFile; let currentText: string; + let currentFileIdentifiers: Map; let extendsEmitted: boolean; let decorateEmitted: boolean; let paramEmitted: boolean; @@ -169,6 +169,8 @@ const _super = (function (geti, seti) { function doPrint(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); + nodeToGeneratedName = []; + generatedNameSet = {}; isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files @@ -213,8 +215,6 @@ const _super = (function (geti, seti) { identifierSubstitution = undefined; onBeforeEmitNode = undefined; onAfterEmitNode = undefined; - isUniqueName = undefined; - temporaryVariables = undefined; tempFlags = TempFlags.Auto; currentSourceFile = undefined; currentText = undefined; @@ -236,13 +236,13 @@ const _super = (function (geti, seti) { identifierSubstitution = context.identifierSubstitution; onBeforeEmitNode = context.onBeforeEmitNode; onAfterEmitNode = context.onAfterEmitNode; - isUniqueName = context.isUniqueName; return printSourceFile; } function printSourceFile(node: SourceFile) { currentSourceFile = node; currentText = node.text; + currentFileIdentifiers = node.identifiers; sourceMap.setSourceFile(node); comments.setSourceFile(node); emitWorker(node); @@ -659,22 +659,11 @@ const _super = (function (geti, seti) { // function emitIdentifier(node: Identifier) { - if (node.text === undefined) { - // Emit a temporary variable name for this node. - const nodeId = getOriginalNodeId(node); - const text = temporaryVariables[nodeId] || (temporaryVariables[nodeId] = makeTempVariableName(tempKindToFlags(node.tempKind))); - write(text); - } - else if (nodeIsSynthesized(node) || !node.parent) { - if (getNodeEmitFlags(node) & NodeEmitFlags.UMDDefine) { - writeLines(umdHelper); - } - else { - write(node.text); - } + if (getNodeEmitFlags(node) && NodeEmitFlags.UMDDefine) { + writeLines(umdHelper); } else { - writeTextOfNode(currentText, node); + write(getTextOfNode(node, /*includeTrivia*/ false)); } } @@ -1720,7 +1709,6 @@ const _super = (function (geti, seti) { emitExpression(node.expression); write(":"); - debugger; emitCaseOrDefaultClauseStatements(node, node.statements); } @@ -1763,14 +1751,14 @@ const _super = (function (geti, seti) { function emitPropertyAssignment(node: PropertyAssignment) { emit(node.name); write(": "); - // // This is to ensure that we emit comment in the following case: - // // For example: - // // obj = { - // // id: /*comment1*/ ()=>void - // // } - // // "comment1" is not considered to be leading comment for node.initializer - // // but rather a trailing comment on the previous node. - // emitTrailingCommentsOfPosition(node.initializer.pos); + // This is to ensure that we emit comment in the following case: + // For example: + // obj = { + // id: /*comment1*/ ()=>void + // } + // "comment1" is not considered to be leading comment for node.initializer + // but rather a trailing comment on the previous node. + emitLeadingComments(node.initializer, getTrailingComments(collapseTextRange(node.initializer, TextRangeCollapse.CollapseToStart))); emitExpression(node.initializer); } @@ -1951,11 +1939,8 @@ const _super = (function (geti, seti) { } function emitModifiers(node: Node, modifiers: ModifiersArray) { - const startingPos = writer.getTextPos(); - emitList(node, modifiers, ListFormat.SingleLine); - - const endingPos = writer.getTextPos(); - if (startingPos !== endingPos) { + if (modifiers && modifiers.length) { + emitList(node, modifiers, ListFormat.SingleLine); write(" "); } } @@ -2345,7 +2330,15 @@ const _super = (function (geti, seti) { } function getTextOfNode(node: Node, includeTrivia?: boolean) { - if (nodeIsSynthesized(node) && (isLiteralExpression(node) || isIdentifier(node))) { + if (isIdentifier(node)) { + if (node.autoGenerateKind) { + return getGeneratedIdentifier(node); + } + else if (nodeIsSynthesized(node) || !node.parent) { + return node.text; + } + } + else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { return node.text; } @@ -2368,10 +2361,22 @@ const _super = (function (geti, seti) { && rangeEndIsOnSameLineAsRangeStart(block, block); } - function tempKindToFlags(kind: TempVariableKind) { - return kind === TempVariableKind.Loop - ? TempFlags._i - : TempFlags.Auto; + function isUniqueName(name: string): boolean { + return !resolver.hasGlobalName(name) && + !hasProperty(currentFileIdentifiers, name) && + !hasProperty(generatedNameSet, name); + } + + function isUniqueLocalName(name: string, container: Node): boolean { + for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { + if (node.locals && hasProperty(node.locals, name)) { + // We conservatively include alias symbols to cover cases where they're emitted as locals + if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + return false; + } + } + } + return true; } /** @@ -2401,6 +2406,85 @@ const _super = (function (geti, seti) { } } } + + // Generate a name that is unique within the current file and doesn't conflict with any names + // in global scope. The name is formed by adding an '_n' suffix to the specified base name, + // where n is a positive integer. Note that names generated by makeTempVariableName and + // makeUniqueName are guaranteed to never conflict. + function makeUniqueName(baseName: string): string { + // Find the first unique 'name_n', where n is a positive number + if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { + baseName += "_"; + } + let i = 1; + while (true) { + const generatedName = baseName + i; + if (isUniqueName(generatedName)) { + return generatedNameSet[generatedName] = generatedName; + } + i++; + } + } + + function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { + const name = node.name.text; + // Use module/enum name itself if it is unique, otherwise make a unique variation + return isUniqueLocalName(name, node) ? name : makeUniqueName(name); + } + + function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { + const expr = getExternalModuleName(node); + const baseName = expr.kind === SyntaxKind.StringLiteral ? + escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; + return makeUniqueName(baseName); + } + + function generateNameForExportDefault() { + return makeUniqueName("default"); + } + + function generateNameForClassExpression() { + return makeUniqueName("class"); + } + + function generateNameForNode(node: Node) { + switch (node.kind) { + case SyntaxKind.Identifier: + return makeUniqueName((node).text); + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.EnumDeclaration: + return generateNameForModuleOrEnum(node); + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + return generateNameForImportOrExportDeclaration(node); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ExportAssignment: + return generateNameForExportDefault(); + case SyntaxKind.ClassExpression: + return generateNameForClassExpression(); + default: + return makeTempVariableName(TempFlags.Auto); + } + } + + function generateIdentifier(node: Identifier) { + switch (node.autoGenerateKind) { + case GeneratedIdentifierKind.Auto: + return makeTempVariableName(TempFlags.Auto); + case GeneratedIdentifierKind.Loop: + return makeTempVariableName(TempFlags._i); + case GeneratedIdentifierKind.Unique: + return makeUniqueName(node.text); + case GeneratedIdentifierKind.Node: + return generateNameForNode(getOriginalNode(node)); + } + } + + function getGeneratedIdentifier(node: Identifier) { + const id = getOriginalNodeId(node); + return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateIdentifier(node))); + } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 65d61c044c2..98b71f0c16e 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -22,8 +22,6 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) { - const nodeToGeneratedName: Identifier[] = []; - const generatedNameSet: Map = {}; const nodeEmitFlags: NodeEmitFlags[] = []; const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; @@ -40,10 +38,6 @@ namespace ts { getEmitResolver: () => resolver, getNodeEmitFlags, setNodeEmitFlags, - isUniqueName, - getGeneratedNameForNode, - nodeHasGeneratedName, - makeUniqueName, hoistVariableDeclaration, hoistFunctionDeclaration, startLexicalEnvironment, @@ -119,118 +113,6 @@ namespace ts { return node; } - /** - * Generate a name that is unique within the current file and doesn't conflict with any names - * in global scope. The name is formed by adding an '_n' suffix to the specified base name, - * where n is a positive integer. Note that names generated by makeTempVariableName and - * makeUniqueName are guaranteed to never conflict. - */ - function makeUniqueName(baseName: string): Identifier { - // Find the first unique 'name_n', where n is a positive number - if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { - baseName += "_"; - } - - let i = 1; - while (true) { - const generatedName = baseName + i; - if (isUniqueName(generatedName)) { - return createIdentifier(generatedNameSet[generatedName] = generatedName); - } - - i++; - } - } - - /** - * Gets the generated name for a node. - */ - function getGeneratedNameForNode(node: Node) { - const id = getNodeId(node); - return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = generateNameForNode(node)); - } - - /** - * Gets a value indicating whether a node has a generated name. - */ - function nodeHasGeneratedName(node: Node) { - const id = getNodeId(node); - return nodeToGeneratedName[id] !== undefined; - } - - /** - * Tests whether the provided name is unique. - */ - function isUniqueName(name: string): boolean { - return !resolver.hasGlobalName(name) - && !hasProperty(currentSourceFile.identifiers, name) - && !hasProperty(generatedNameSet, name); - } - - /** - * Tests whether the provided name is unique within a container. - */ - function isUniqueLocalName(name: string, container: Node): boolean { - container = getOriginalNode(container); - for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { - // We conservatively include alias symbols to cover cases where they're emitted as locals - if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { - return false; - } - } - } - return true; - } - - /** - * Generates a name for a node. - */ - function generateNameForNode(node: Node): Identifier { - switch (node.kind) { - case SyntaxKind.Identifier: - return makeUniqueName((node).text); - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.EnumDeclaration: - return generateNameForModuleOrEnum(node); - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - return generateNameForImportOrExportDeclaration(node); - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - Debug.assert((node.flags & NodeFlags.Default) !== 0, "Can only generate a name for a default export."); - return generateNameForExportDefault(); - case SyntaxKind.ExportAssignment: - return generateNameForExportDefault(); - case SyntaxKind.ClassExpression: - return generateNameForClassExpression(); - default: - return createTempVariable(); - } - } - - function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { - const name = node.name; - // Use module/enum name itself if it is unique, otherwise make a unique variation - return isUniqueLocalName(name.text, node) ? name : makeUniqueName(name.text); - } - - function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { - const expr = getExternalModuleName(node); - const baseName = expr.kind === SyntaxKind.StringLiteral - ? escapeIdentifier(makeIdentifierFromModuleName((expr).text)) - : "module"; - return makeUniqueName(baseName); - } - - function generateNameForExportDefault() { - return makeUniqueName("default"); - } - - function generateNameForClassExpression() { - return makeUniqueName("class"); - } - /** * Records a hoisted variable declaration for the provided name within a lexical environment. */ diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6a55153f631..fb5ab0c3609 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -497,16 +497,19 @@ namespace ts { // @kind(SyntaxKind.StaticKeyword) export interface Modifier extends Node { } - export const enum TempVariableKind { - Auto, // Automatically generated identifier - Loop, // Automatically generated identifier with a preference for '_i' + export const enum GeneratedIdentifierKind { + None, // Not automatically generated. + Auto, // Automatically generated identifier. + Loop, // Automatically generated identifier with a preference for '_i'. + Unique, // Unique name based on the 'text' property. + Node, // Unique name based on the node in the 'original' property. } // @kind(SyntaxKind.Identifier) export interface Identifier extends PrimaryExpression { text: string; // Text of identifier (with escapes converted to characters) - tempKind?: TempVariableKind; // Specifies whether to auto-generate the text for an identifier. originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. } // @kind(SyntaxKind.QualifiedName) @@ -2805,7 +2808,7 @@ namespace ts { SingleLine = 1 << 6, // The contents of this node should be emit on a single line. AdviseOnEmitNode = 1 << 7, // The node printer should invoke the onBeforeEmitNode and onAfterEmitNode callbacks when printing this node. IsNotEmittedNode = 1 << 8, // Is a node that is not emitted but whose comments should be preserved if possible. - EmitCommentsOfNotEmittedParent = 1 << 8, // Emits comments of missing parent nodes. + EmitCommentsOfNotEmittedParent = 1 << 9, // Emits comments of missing parent nodes. } /** Additional context provided to `visitEachChild` */ @@ -2825,10 +2828,6 @@ namespace ts { setNodeEmitFlags(node: T, flags: NodeEmitFlags): T; hoistFunctionDeclaration(node: FunctionDeclaration): void; hoistVariableDeclaration(node: Identifier): void; - isUniqueName(name: string): boolean; - getGeneratedNameForNode(node: Node): Identifier; - nodeHasGeneratedName(node: Node): boolean; - makeUniqueName(baseName: string): Identifier; /** * Hook used by transformers to substitute non-expression identifiers diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 10b5b467158..514ee5f258d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2297,11 +2297,11 @@ namespace ts { writer.write(" "); } - let emitInterveningSeperator = false; + let emitInterveningSeparator = false; for (const comment of comments) { - if (emitInterveningSeperator) { + if (emitInterveningSeparator) { writer.write(" "); - emitInterveningSeperator = false; + emitInterveningSeparator = false; } writeComment(text, lineMap, writer, comment, newLine); @@ -2309,11 +2309,11 @@ namespace ts { writer.writeLine(); } else { - emitInterveningSeperator = true; + emitInterveningSeparator = true; } } - if (emitInterveningSeperator && trailingSeparator) { + if (emitInterveningSeparator && trailingSeparator) { writer.write(" "); } } @@ -2723,7 +2723,7 @@ namespace ts { } return collapse === TextRangeCollapse.CollapseToStart - ? { pos: range.pos, end: range.end } + ? { pos: range.pos, end: range.pos } : { pos: range.end, end: range.end }; }