Merge pull request #2471 from Microsoft/revisedNameGen2

Revised name generation #2, fixes #2449, #2447
This commit is contained in:
Vladimir Matveev
2015-03-24 15:20:18 -07:00
95 changed files with 1003 additions and 913 deletions

View File

@@ -5,6 +5,12 @@ module ts {
let nextNodeId = 1;
let nextMergeId = 1;
// @internal
export function getNodeId(node: Node): number {
if (!node.id) node.id = nextNodeId++;
return node.id;
}
/* @internal */ export let checkTime = 0;
/* @internal */
@@ -264,8 +270,8 @@ module ts {
}
function getNodeLinks(node: Node): NodeLinks {
if (!node.id) node.id = nextNodeId++;
return nodeLinks[node.id] || (nodeLinks[node.id] = {});
let nodeId = getNodeId(node);
return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
}
function getSourceFile(node: Node): SourceFile {
@@ -10963,134 +10969,7 @@ module ts {
return symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length === 1 && symbol.declarations[0].kind === SyntaxKind.SourceFile;
}
function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
while (node) {
if (node === ancestor) return true;
node = node.parent;
}
return false;
}
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;
}
function getGeneratedNamesForSourceFile(sourceFile: SourceFile): Map<string> {
let links = getNodeLinks(sourceFile);
let generatedNames = links.generatedNames;
if (!generatedNames) {
generatedNames = links.generatedNames = {};
generateNames(sourceFile);
}
return generatedNames;
function generateNames(node: Node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
generateNameForFunctionOrClassDeclaration(<Declaration>node);
break;
case SyntaxKind.ModuleDeclaration:
generateNameForModuleOrEnum(<ModuleDeclaration>node);
generateNames((<ModuleDeclaration>node).body);
break;
case SyntaxKind.EnumDeclaration:
generateNameForModuleOrEnum(<EnumDeclaration>node);
break;
case SyntaxKind.ImportDeclaration:
generateNameForImportDeclaration(<ImportDeclaration>node);
break;
case SyntaxKind.ExportDeclaration:
generateNameForExportDeclaration(<ExportDeclaration>node);
break;
case SyntaxKind.ExportAssignment:
generateNameForExportAssignment(<ExportAssignment>node);
break;
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
forEach((<SourceFile | ModuleBlock>node).statements, generateNames);
break;
}
}
function isExistingName(name: string) {
return hasProperty(globals, name) || hasProperty(sourceFile.identifiers, name) || hasProperty(generatedNames, name);
}
function makeUniqueName(baseName: string): string {
let name = generateUniqueName(baseName, isExistingName);
return generatedNames[name] = name;
}
function assignGeneratedName(node: Node, name: string) {
getNodeLinks(node).generatedName = unescapeIdentifier(name);
}
function generateNameForFunctionOrClassDeclaration(node: Declaration) {
if (!node.name) {
assignGeneratedName(node, makeUniqueName("default"));
}
}
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
if (node.name.kind === SyntaxKind.Identifier) {
let name = node.name.text;
// Use module/enum name itself if it is unique, otherwise make a unique variation
assignGeneratedName(node, isUniqueLocalName(name, node) ? name : makeUniqueName(name));
}
}
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
let expr = getExternalModuleName(node);
let baseName = expr.kind === SyntaxKind.StringLiteral ?
escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text)) : "module";
assignGeneratedName(node, makeUniqueName(baseName));
}
function generateNameForImportDeclaration(node: ImportDeclaration) {
if (node.importClause && node.importClause.namedBindings && node.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
generateNameForImportOrExportDeclaration(node);
}
}
function generateNameForExportDeclaration(node: ExportDeclaration) {
if (node.moduleSpecifier) {
generateNameForImportOrExportDeclaration(node);
}
}
function generateNameForExportAssignment(node: ExportAssignment) {
if (node.expression && node.expression.kind !== SyntaxKind.Identifier) {
assignGeneratedName(node, makeUniqueName("default"));
}
}
}
function getGeneratedNameForNode(node: Node) {
let links = getNodeLinks(node);
if (!links.generatedName) {
getGeneratedNamesForSourceFile(getSourceFile(node));
}
return links.generatedName;
}
function getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string {
return getGeneratedNameForNode(container);
}
function getLocalNameForImportDeclaration(node: ImportDeclaration): string {
return getGeneratedNameForNode(node);
}
function getAliasNameSubstitution(symbol: Symbol): string {
function getAliasNameSubstitution(symbol: Symbol, getGeneratedNameForNode: (Node: Node) => string): string {
let declaration = getDeclarationOfAliasSymbol(symbol);
if (declaration && declaration.kind === SyntaxKind.ImportSpecifier) {
let moduleName = getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent);
@@ -11099,7 +10978,7 @@ module ts {
}
}
function getExportNameSubstitution(symbol: Symbol, location: Node): string {
function getExportNameSubstitution(symbol: Symbol, location: Node, getGeneratedNameForNode: (Node: Node) => string): string {
if (isExternalModuleSymbol(symbol.parent)) {
var symbolName = unescapeIdentifier(symbol.name);
// If this is es6 or higher, just use the name of the export
@@ -11121,24 +11000,24 @@ module ts {
}
}
function getExpressionNameSubstitution(node: Identifier): string {
function getExpressionNameSubstitution(node: Identifier, getGeneratedNameForNode: (Node: Node) => string): string {
let symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
// Whan an identifier resolves to a parented symbol, it references an exported entity from
// another declaration of the same internal module.
if (symbol.parent) {
return getExportNameSubstitution(symbol, node.parent);
return getExportNameSubstitution(symbol, node.parent, getGeneratedNameForNode);
}
// If we reference an exported entity within the same module declaration, then whether
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
// kinds that we do NOT prefix.
let exportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
if (symbol !== exportSymbol && !(exportSymbol.flags & SymbolFlags.ExportHasLocal)) {
return getExportNameSubstitution(exportSymbol, node.parent);
return getExportNameSubstitution(exportSymbol, node.parent, getGeneratedNameForNode);
}
// Named imports from ES6 import declarations are rewritten
if (symbol.flags & SymbolFlags.Alias && languageVersion < ScriptTarget.ES6) {
return getAliasNameSubstitution(symbol);
return getAliasNameSubstitution(symbol, getGeneratedNameForNode);
}
}
}
@@ -11241,10 +11120,13 @@ module ts {
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
}
function isUnknownIdentifier(location: Node, name: string): boolean {
Debug.assert(!nodeIsSynthesized(location), "isUnknownIdentifier called with a synthesized location");
return !resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined) &&
!hasProperty(getGeneratedNamesForSourceFile(getSourceFile(location)), name);
function hasGlobalName(name: string): boolean {
return hasProperty(globals, name);
}
function resolvesToSomeValue(location: Node, name: string): boolean {
Debug.assert(!nodeIsSynthesized(location), "resolvesToSomeValue called with a synthesized location");
return !!resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
}
function getBlockScopedVariableId(n: Identifier): number {
@@ -11274,8 +11156,8 @@ module ts {
function createResolver(): EmitResolver {
return {
getGeneratedNameForNode,
getExpressionNameSubstitution,
hasGlobalName,
hasExportDefaultValue,
isReferencedAliasDeclaration,
getNodeCheckFlags,
@@ -11288,7 +11170,7 @@ module ts {
isSymbolAccessible,
isEntityNameVisible,
getConstantValue,
isUnknownIdentifier,
resolvesToSomeValue,
collectLinkedAliases,
getBlockScopedVariableId,
};

View File

@@ -19,6 +19,14 @@ module ts {
return isExternalModule(sourceFile) || isDeclarationFile(sourceFile);
}
// flag enum used to request and track usages of few dedicated temp variables
// enum values are used to set/check bit values and thus should not have bit collisions.
const enum TempVariableKind {
auto = 0,
_i = 1,
_n = 2,
}
// @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 {
@@ -60,6 +68,26 @@ module ts {
sourceMaps: sourceMapDataList
};
function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
while (node) {
if (node === ancestor) return true;
node = node.parent;
}
return false;
}
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;
}
function emitJavaScript(jsFilePath: string, root?: SourceFile) {
let writer = createTextWriter(newLine);
let write = writer.write;
@@ -71,15 +99,17 @@ module ts {
let currentSourceFile: SourceFile;
let lastFrame: ScopeFrame;
let currentScopeNames: Map<string>;
let generatedBlockScopeNames: string[];
let generatedNameSet: Map<string>;
let nodeToGeneratedName: string[];
let blockScopedVariableToGeneratedName: string[];
let extendsEmitted = false;
let tempCount = 0;
let tempVariables: Identifier[];
let tempParameters: Identifier[];
let predefinedTempsInUse = TempVariableKind.auto;
let externalImports: ExternalImportInfo[];
let exportSpecifiers: Map<ExportSpecifier[]>;
let exportDefault: FunctionDeclaration | ClassDeclaration | ExportAssignment | ExportSpecifier;
@@ -144,81 +174,197 @@ module ts {
emit(sourceFile);
}
// enters the new lexical environment
// return value should be passed to matching call to exitNameScope.
function enterNameScope(): boolean {
let names = currentScopeNames;
currentScopeNames = undefined;
if (names) {
lastFrame = { names, previous: lastFrame };
return true;
function generateNameForNode(node: Node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
generateNameForFunctionOrClassDeclaration(<Declaration>node);
break;
case SyntaxKind.ModuleDeclaration:
generateNameForModuleOrEnum(<ModuleDeclaration>node);
generateNameForNode((<ModuleDeclaration>node).body);
break;
case SyntaxKind.EnumDeclaration:
generateNameForModuleOrEnum(<EnumDeclaration>node);
break;
case SyntaxKind.ImportDeclaration:
generateNameForImportDeclaration(<ImportDeclaration>node);
break;
case SyntaxKind.ExportDeclaration:
generateNameForExportDeclaration(<ExportDeclaration>node);
break;
case SyntaxKind.ExportAssignment:
generateNameForExportAssignment(<ExportAssignment>node);
break;
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
forEach((<SourceFile | ModuleBlock>node).statements, generateNameForNode);
break;
}
return false;
}
function exitNameScope(popFrame: boolean): void {
if (popFrame) {
currentScopeNames = lastFrame.names;
lastFrame = lastFrame.previous;
function isUniqueName(name: string): boolean {
return !resolver.hasGlobalName(name) &&
!hasProperty(currentSourceFile.identifiers, name) &&
(!generatedNameSet || !hasProperty(generatedNameSet, name))
}
// in cases like
// for (var x of []) {
// _i;
// }
// we should be able to detect if let _i was shadowed by some temp variable that was allocated in scope
function nameConflictsWithSomeTempVariable(name: string): boolean {
// temp variable names always start with '_'
if (name.length < 2 || name.charCodeAt(0) !== CharacterCodes._) {
return false;
}
if (name === "_i") {
return !!(predefinedTempsInUse & TempVariableKind._i);
}
if (name === "_n") {
return !!(predefinedTempsInUse & TempVariableKind._n);
}
if (name.length === 2 && name.charCodeAt(1) >= CharacterCodes.a && name.charCodeAt(1) <= CharacterCodes.z) {
// handles _a .. _z
let n = name.charCodeAt(1) - CharacterCodes.a;
return n < tempCount;
}
else {
currentScopeNames = undefined;
// handles _1, _2...
let n = +name.substring(1);
return !isNaN(n) && n >= 0 && n < (tempCount - 26);
}
}
function generateUniqueNameForLocation(location: Node, baseName: string): string {
let 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));
}
return recordNameInCurrentScope(name);
}
function recordNameInCurrentScope(name: string): string {
if (!currentScopeNames) {
currentScopeNames = {};
}
return currentScopeNames[name] = name;
}
function isExistingName(location: Node, name: string) {
// check if resolver is aware of this name (if name was seen during the typecheck)
if (!resolver.isUnknownIdentifier(location, name)) {
return true;
}
// check if name is present in generated names that were introduced by the emitter
if (currentScopeNames && hasProperty(currentScopeNames, name)) {
return true;
}
// check generated names in outer scopes
// let x;
// function foo() {
// let x; // 1
// function bar() {
// {
// let x; // 2
// }
// console.log(x); // 3
// }
//}
// here both x(1) and x(2) should be renamed and their names should be different
// so x in (3) will refer to x(1)
let frame = lastFrame;
while (frame) {
if (hasProperty(frame.names, name)) {
return true;
// This function generates a name using the following pattern:
// _a .. _h, _j ... _z, _0, _1, ...
// It is guaranteed that generated name will not shadow any existing user-defined names,
// however it can hide another name generated by this function higher in the scope.
// NOTE: names generated by 'makeTempVariableName' and 'makeUniqueName' will never conflict.
// see comment for 'makeTempVariableName' for more information.
function makeTempVariableName(location: Node, tempVariableKind: TempVariableKind): string {
let tempName: string;
if (tempVariableKind !== TempVariableKind.auto && !(predefinedTempsInUse & tempVariableKind)) {
tempName = tempVariableKind === TempVariableKind._i ? "_i" : "_n";
if (!resolver.resolvesToSomeValue(location, tempName)) {
predefinedTempsInUse |= tempVariableKind;
return tempName;
}
frame = frame.previous;
}
return false;
do {
// Note: we avoid generating _i and _n as those are common names we want in other places.
var char = CharacterCodes.a + tempCount;
if (char !== CharacterCodes.i && char !== CharacterCodes.n) {
if (tempCount < 26) {
tempName = "_" + String.fromCharCode(char);
}
else {
tempName = "_" + (tempCount - 26);
}
}
tempCount++;
}
while (resolver.resolvesToSomeValue(location, tempName));
return tempName;
}
// Generates a name that is unique within current file and does not collide with
// any names in global scope.
// NOTE: names generated by 'makeTempVariableName' and 'makeUniqueName' will never conflict
// because of the way how these names are generated
// - makeUniqueName builds a name by picking a base name (which should not be empty string)
// and appending suffix '_<number>'
// - makeTempVariableName creates a name using the following pattern:
// _a .. _h, _j ... _z, _0, _1, ...
// This means that names from 'makeTempVariableName' will have only one underscore at the beginning
// and names from 'makeUniqieName' will have at least one underscore in the middle
// so they will never collide.
function makeUniqueName(baseName: string): string {
Debug.assert(!!baseName);
// Find the first unique 'name_n', where n is a positive number
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
baseName += "_";
}
let i = 1;
let generatedName: string;
while (true) {
generatedName = baseName + i;
if (isUniqueName(generatedName)) {
break;
}
i++;
}
if (!generatedNameSet) {
generatedNameSet = {};
}
return generatedNameSet[generatedName] = generatedName;
}
function renameNode(node: Node, name: string): string {
var nodeId = getNodeId(node);
if (!nodeToGeneratedName) {
nodeToGeneratedName = [];
}
return nodeToGeneratedName[nodeId] = unescapeIdentifier(name);
}
function generateNameForFunctionOrClassDeclaration(node: Declaration) {
if (!node.name) {
renameNode(node, makeUniqueName("default"));
}
}
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
if (node.name.kind === SyntaxKind.Identifier) {
let name = node.name.text;
// Use module/enum name itself if it is unique, otherwise make a unique variation
renameNode(node, isUniqueLocalName(name, node) ? name : makeUniqueName(name));
}
}
function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
let expr = getExternalModuleName(node);
let baseName = expr.kind === SyntaxKind.StringLiteral ?
escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text)) : "module";
renameNode(node, makeUniqueName(baseName));
}
function generateNameForImportDeclaration(node: ImportDeclaration) {
if (node.importClause && node.importClause.namedBindings && node.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
generateNameForImportOrExportDeclaration(node);
}
}
function generateNameForExportDeclaration(node: ExportDeclaration) {
if (node.moduleSpecifier) {
generateNameForImportOrExportDeclaration(node);
}
}
function generateNameForExportAssignment(node: ExportAssignment) {
if (node.expression && node.expression.kind !== SyntaxKind.Identifier) {
renameNode(node, makeUniqueName("default"));
}
}
function getGeneratedNameForNode(node: Node) {
let nodeId = getNodeId(node);
if (!nodeToGeneratedName || !nodeToGeneratedName[nodeId]) {
generateNameForNode(node);
}
return nodeToGeneratedName ? nodeToGeneratedName[nodeId] : undefined;
}
function initializeEmitterWithSourceMaps() {
@@ -585,32 +731,10 @@ module ts {
writeFile(host, diagnostics, jsFilePath, emitOutput, writeByteOrderMark);
}
// Create a temporary variable with a unique unused name. The forLoopVariable parameter signals that the
// name should be one that is appropriate for a for loop variable.
function createTempVariable(location: Node, preferredName?: string): Identifier {
for (var name = preferredName; !name || isExistingName(location, name); tempCount++) {
// _a .. _h, _j ... _z, _0, _1, ...
// Note: we avoid generating _i and _n as those are common names we want in other places.
var char = CharacterCodes.a + tempCount;
if (char === CharacterCodes.i || char === CharacterCodes.n) {
continue;
}
if (tempCount < 26) {
name = "_" + String.fromCharCode(char);
}
else {
name = "_" + (tempCount - 26);
}
}
// This is necessary so that a name generated via renameNonTopLevelLetAndConst will see the name
// we just generated.
recordNameInCurrentScope(name);
// Create a temporary variable with a unique unused name.
function createTempVariable(location: Node, tempVariableKind = TempVariableKind.auto): Identifier {
let result = <Identifier>createSynthesizedNode(SyntaxKind.Identifier);
result.text = name;
result.text = makeTempVariableName(location, tempVariableKind);
return result;
}
@@ -621,8 +745,8 @@ module ts {
tempVariables.push(name);
}
function createAndRecordTempVariable(location: Node, preferredName?: string): Identifier {
let temp = createTempVariable(location, preferredName);
function createAndRecordTempVariable(location: Node, tempVariableKind?: TempVariableKind): Identifier {
let temp = createTempVariable(location, tempVariableKind);
recordTempDeclaration(temp);
return temp;
@@ -1085,7 +1209,7 @@ module ts {
}
function emitExpressionIdentifier(node: Identifier) {
let substitution = resolver.getExpressionNameSubstitution(node);
let substitution = resolver.getExpressionNameSubstitution(node, getGeneratedNameForNode);
if (substitution) {
write(substitution);
}
@@ -1095,7 +1219,7 @@ module ts {
}
function getGeneratedNameForIdentifier(node: Identifier): string {
if (nodeIsSynthesized(node) || !generatedBlockScopeNames) {
if (nodeIsSynthesized(node) || !blockScopedVariableToGeneratedName) {
return undefined;
}
@@ -1104,7 +1228,7 @@ module ts {
return undefined;
}
return generatedBlockScopeNames[variableId];
return blockScopedVariableToGeneratedName[variableId];
}
function emitIdentifier(node: Identifier, allowGeneratedIdentifiers: boolean) {
@@ -1332,7 +1456,7 @@ module ts {
// manage by just emitting strings (which is a lot more performant).
//let prefix = createIdentifier(resolver.getExpressionNamePrefix((<ShorthandPropertyAssignment>property).name));
//return createPropertyAccessExpression(prefix, (<ShorthandPropertyAssignment>property).name);
return createIdentifier(resolver.getExpressionNameSubstitution((<ShorthandPropertyAssignment>property).name));
return createIdentifier(resolver.getExpressionNameSubstitution((<ShorthandPropertyAssignment>property).name, getGeneratedNameForNode));
case SyntaxKind.MethodDeclaration:
return createFunctionExpression((<MethodDeclaration>property).parameters, (<MethodDeclaration>property).body);
@@ -1546,7 +1670,7 @@ module ts {
emitExpressionIdentifier(node.name);
}
}
else if (resolver.getExpressionNameSubstitution(node.name)) {
else if (resolver.getExpressionNameSubstitution(node.name, getGeneratedNameForNode)) {
// Emit identifier as an identifier
write(": ");
// Even though this is stored as identifier treat it as an expression
@@ -2079,10 +2203,10 @@ module ts {
//
// we don't want to emit a temporary variable for the RHS, just use it directly.
let rhsIsIdentifier = node.expression.kind === SyntaxKind.Identifier;
let counter = createTempVariable(node, /*preferredName*/ "_i");
let counter = createTempVariable(node, TempVariableKind._i);
let rhsReference = rhsIsIdentifier ? <Identifier>node.expression : createTempVariable(node);
var cachedLength = compilerOptions.cacheDownlevelForOfLength ? createTempVariable(node, /*preferredName:*/ "_n") : undefined;
var cachedLength = compilerOptions.cacheDownlevelForOfLength ? createTempVariable(node, TempVariableKind._n) : undefined;
// This is the let keyword for the counter and rhsReference. The let keyword for
// the LHS will be emitted inside the body.
@@ -2323,7 +2447,7 @@ module ts {
function emitContainingModuleName(node: Node) {
let container = getContainingModule(node);
write(container ? resolver.getGeneratedNameForNode(container) : "exports");
write(container ? getGeneratedNameForNode(container) : "exports");
}
function emitModuleMemberName(node: Declaration) {
@@ -2331,7 +2455,7 @@ module ts {
if (getCombinedNodeFlags(node) & NodeFlags.Export) {
var container = getContainingModule(node);
if (container) {
write(resolver.getGeneratedNameForNode(container));
write(getGeneratedNameForNode(container));
write(".");
}
else if (languageVersion < ScriptTarget.ES6) {
@@ -2673,9 +2797,14 @@ module ts {
// here it is known that node is a block scoped variable
let 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;
if (list.parent.kind === SyntaxKind.VariableStatement) {
let isSourceFileLevelBinding = list.parent.parent.kind === SyntaxKind.SourceFile;
let isModuleLevelBinding = list.parent.parent.kind === SyntaxKind.ModuleBlock;
let isFunctionLevelBinding =
list.parent.parent.kind === SyntaxKind.Block && isFunctionLike(list.parent.parent.parent);
if (isSourceFileLevelBinding || isModuleLevelBinding || isFunctionLevelBinding) {
return;
}
}
let blockScopeContainer = getEnclosingBlockScopeContainer(node);
@@ -2683,12 +2812,19 @@ module ts {
? blockScopeContainer
: blockScopeContainer.parent;
let generatedName = generateUniqueNameForLocation(parent, (<Identifier>node).text);
let variableId = resolver.getBlockScopedVariableId(<Identifier>node);
if (!generatedBlockScopeNames) {
generatedBlockScopeNames = [];
var hasConflictsInEnclosingScope =
resolver.resolvesToSomeValue(parent, (<Identifier>node).text) ||
nameConflictsWithSomeTempVariable((<Identifier>node).text);
if (hasConflictsInEnclosingScope) {
let variableId = resolver.getBlockScopedVariableId(<Identifier>node);
if (!blockScopedVariableToGeneratedName) {
blockScopedVariableToGeneratedName = [];
}
let generatedName = makeUniqueName((<Identifier>node).text);
blockScopedVariableToGeneratedName[variableId] = generatedName;
}
generatedBlockScopeNames[variableId] = generatedName;
}
function isES6ModuleMemberDeclaration(node: Node) {
@@ -2770,7 +2906,7 @@ module ts {
if (languageVersion < ScriptTarget.ES6 && hasRestParameters(node)) {
let restIndex = node.parameters.length - 1;
let restParam = node.parameters[restIndex];
let tempName = createTempVariable(node, /*preferredName:*/ "_i").text;
let tempName = createTempVariable(node, TempVariableKind._i).text;
writeLine();
emitLeadingComments(restParam);
emitStart(restParam);
@@ -2820,7 +2956,7 @@ module ts {
emitNodeWithoutSourceMap(node.name);
}
else {
write(resolver.getGeneratedNameForNode(node));
write(getGeneratedNameForNode(node));
}
}
@@ -2906,11 +3042,12 @@ module ts {
let saveTempCount = tempCount;
let saveTempVariables = tempVariables;
let saveTempParameters = tempParameters;
let savePredefinedTempsInUse = predefinedTempsInUse;
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
let popFrame = enterNameScope()
predefinedTempsInUse = TempVariableKind.auto;
// When targeting ES6, emit arrow function natively in ES6
if (shouldEmitAsArrowFunction(node)) {
@@ -2943,8 +3080,7 @@ module ts {
write(";");
}
exitNameScope(popFrame);
predefinedTempsInUse = savePredefinedTempsInUse;
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
@@ -3241,11 +3377,12 @@ module ts {
let saveTempCount = tempCount;
let saveTempVariables = tempVariables;
let saveTempParameters = tempParameters;
let savePredefinedTempsInUse = predefinedTempsInUse;
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
predefinedTempsInUse = TempVariableKind.auto;
let popFrame = enterNameScope();
// Check if we have property assignment inside class declaration.
// If there is property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
@@ -3354,8 +3491,7 @@ module ts {
emitTrailingComments(ctor);
}
exitNameScope(popFrame);
predefinedTempsInUse = savePredefinedTempsInUse;
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
@@ -3494,7 +3630,7 @@ module ts {
emitStart(node);
write("(function (");
emitStart(node.name);
write(resolver.getGeneratedNameForNode(node));
write(getGeneratedNameForNode(node));
emitEnd(node.name);
write(") {");
increaseIndent();
@@ -3531,9 +3667,9 @@ module ts {
function emitEnumMember(node: EnumMember) {
let enumParent = <EnumDeclaration>node.parent;
emitStart(node);
write(resolver.getGeneratedNameForNode(enumParent));
write(getGeneratedNameForNode(enumParent));
write("[");
write(resolver.getGeneratedNameForNode(enumParent));
write(getGeneratedNameForNode(enumParent));
write("[");
emitExpressionForPropertyName(node.name);
write("] = ");
@@ -3586,19 +3722,20 @@ module ts {
emitStart(node);
write("(function (");
emitStart(node.name);
write(resolver.getGeneratedNameForNode(node));
write(getGeneratedNameForNode(node));
emitEnd(node.name);
write(") ");
if (node.body.kind === SyntaxKind.ModuleBlock) {
let saveTempCount = tempCount;
let saveTempVariables = tempVariables;
let savePredefinedTempsInUse = predefinedTempsInUse;
tempCount = 0;
tempVariables = undefined;
let popFrame = enterNameScope();
predefinedTempsInUse = TempVariableKind.auto;
emit(node.body);
exitNameScope(popFrame);
predefinedTempsInUse = savePredefinedTempsInUse;
tempCount = saveTempCount;
tempVariables = saveTempVariables;
}
@@ -3762,7 +3899,7 @@ module ts {
}
else if (namedImports) {
write("var ");
write(resolver.getGeneratedNameForNode(<ImportDeclaration>node));
write(getGeneratedNameForNode(<ImportDeclaration>node));
write(" = ");
emitRequire(moduleName);
}
@@ -3817,7 +3954,7 @@ module ts {
if (languageVersion < ScriptTarget.ES6 || node.parent.kind !== SyntaxKind.SourceFile) {
if (node.moduleSpecifier) {
emitStart(node);
let generatedName = resolver.getGeneratedNameForNode(node);
let generatedName = getGeneratedNameForNode(node);
if (compilerOptions.module !== ModuleKind.AMD) {
write("var ");
write(generatedName);
@@ -3920,7 +4057,7 @@ module ts {
return {
rootNode: <ImportDeclaration>node,
namedImports: <NamedImports>importClause.namedBindings,
localName: resolver.getGeneratedNameForNode(<ImportDeclaration>node)
localName: getGeneratedNameForNode(<ImportDeclaration>node)
};
}
}
@@ -4025,7 +4162,7 @@ module ts {
emit(info.declarationNode.name);
}
else {
write(resolver.getGeneratedNameForNode(<ImportDeclaration | ExportDeclaration>info.rootNode));
write(getGeneratedNameForNode(<ImportDeclaration | ExportDeclaration>info.rootNode));
}
});
forEach(node.amdDependencies, amdDependency => {

View File

@@ -1203,8 +1203,8 @@ module ts {
}
export interface EmitResolver {
getGeneratedNameForNode(node: Node): string;
getExpressionNameSubstitution(node: Identifier): string;
hasGlobalName(name: string): boolean;
getExpressionNameSubstitution(node: Identifier, getGeneratedNameForNode: (node: Node) => string): string;
hasExportDefaultValue(node: SourceFile): boolean;
isReferencedAliasDeclaration(node: Node): boolean;
isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean;
@@ -1219,7 +1219,7 @@ module ts {
isEntityNameVisible(entityName: EntityName, enclosingDeclaration: Node): SymbolVisibilityResult;
// 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;
resolvesToSomeValue(location: Node, name: string): boolean;
getBlockScopedVariableId(node: Identifier): number;
}

View File

@@ -1212,28 +1212,6 @@ module ts {
return node;
}
export function generateUniqueName(baseName: string, isExistingName: (name: string) => boolean): string {
// First try '_name'
if (baseName.charCodeAt(0) !== CharacterCodes._) {
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 += "_";
}
let i = 1;
while (true) {
let name = baseName + i;
if (!isExistingName(name)) {
return name;
}
i++;
}
}
// @internal
export function createDiagnosticCollection(): DiagnosticCollection {
let nonFileDiagnostics: Diagnostic[] = [];