PR Feedback and defer makeUniqueName/getGeneratedNameForNode to printer.

This commit is contained in:
Ron Buckton 2016-02-29 13:28:16 -08:00
parent 75b2181e17
commit cd2cf7d3c6
5 changed files with 161 additions and 182 deletions

View File

@ -185,24 +185,38 @@ namespace ts {
// Identifiers
export function createIdentifier(text: string): Identifier {
const node = <Identifier>createNode(SyntaxKind.Identifier);
export function createIdentifier(text: string, location?: TextRange): Identifier {
const node = <Identifier>createNode(SyntaxKind.Identifier, location);
node.text = text;
return node;
}
export function createTempVariable(): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier);
name.text = undefined;
name.tempKind = TempVariableKind.Auto;
export function createTempVariable(location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.autoGenerateKind = GeneratedIdentifierKind.Auto;
getNodeId(name);
return name;
}
export function createLoopVariable(): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier);
name.text = undefined;
name.tempKind = TempVariableKind.Loop;
export function createLoopVariable(location?: TextRange): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier, location);
name.autoGenerateKind = GeneratedIdentifierKind.Loop;
getNodeId(name);
return name;
}
export function createUniqueName(text: string, location?: TextRange): Identifier {
const name = <Identifier>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 = <Identifier>createNode(SyntaxKind.Identifier, location);
name.autoGenerateKind = GeneratedIdentifierKind.Node;
name.original = node;
getNodeId(name);
return name;
}

View File

@ -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<string>;
let tempFlags: TempFlags;
let currentSourceFile: SourceFile;
let currentText: string;
let currentFileIdentifiers: Map<string>;
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((<LiteralExpression>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((<Identifier>node).text);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>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)));
}
}
}

View File

@ -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<string> = {};
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((<Identifier>node).text);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>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((<LiteralExpression>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.
*/

View File

@ -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<T extends Node>(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

View File

@ -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 };
}