Unified logic for renamed block scoped locals and other generated names

This commit is contained in:
Anders Hejlsberg
2015-06-03 06:57:20 -07:00
parent 8b7caedbec
commit 1e39130f41
3 changed files with 112 additions and 136 deletions
+38 -9
View File
@@ -11821,6 +11821,38 @@ module ts {
}
}
function isStatementWithLocals(node: Node) {
switch (node.kind) {
case SyntaxKind.Block:
case SyntaxKind.CaseBlock:
case SyntaxKind.ForStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
return true;
}
return false;
}
function getIsNestedRedeclaration(symbol: Symbol): boolean {
let links = getSymbolLinks(symbol);
if (links.isNestedRedeclaration === undefined) {
let container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
links.isNestedRedeclaration = isStatementWithLocals(container) &&
!!resolveName(container.parent, symbol.name, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
}
return links.isNestedRedeclaration;
}
function getReferencedNestedRedeclaration(node: Identifier): Declaration {
let symbol = getReferencedValueSymbol(node);
return symbol && symbol.flags & SymbolFlags.BlockScoped && getIsNestedRedeclaration(symbol) ? symbol.valueDeclaration : undefined;
}
function isNestedRedeclaration(node: Declaration): boolean {
let symbol = getSymbolOfNode(node);
return symbol.flags & SymbolFlags.BlockScoped && getIsNestedRedeclaration(symbol);
}
function isValueAliasDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
@@ -11924,8 +11956,10 @@ module ts {
/** Serializes an EntityName (with substitutions) to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeEntityName(node: EntityName, getGeneratedNameForNode: (Node: Node) => string, fallbackPath?: string[]): string {
if (node.kind === SyntaxKind.Identifier) {
var substitution = getExpressionNameSubstitution(<Identifier>node, getGeneratedNameForNode);
var text = substitution || (<Identifier>node).text;
// TODO(andersh): Fix this
// var substitution = getExpressionNameSubstitution(<Identifier>node, getGeneratedNameForNode);
// var text = substitution || (<Identifier>node).text;
var text = (<Identifier>node).text;
if (fallbackPath) {
fallbackPath.push(text);
}
@@ -12140,11 +12174,6 @@ module ts {
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 getReferencedValueSymbol(reference: Identifier): Symbol {
return getNodeLinks(reference).resolvedSymbol ||
resolveName(reference, reference.text, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias,
@@ -12198,9 +12227,10 @@ module ts {
function createResolver(): EmitResolver {
return {
getExpressionNameSubstitution,
getReferencedExportContainer,
getReferencedImportDeclaration,
getReferencedNestedRedeclaration,
isNestedRedeclaration,
isValueAliasDeclaration,
hasGlobalName,
isReferencedAliasDeclaration,
@@ -12214,7 +12244,6 @@ module ts {
isSymbolAccessible,
isEntityNameVisible,
getConstantValue,
resolvesToSomeValue,
collectLinkedAliases,
getBlockScopedVariableId,
getReferencedValueDeclaration,
+69 -126
View File
@@ -125,7 +125,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
let generatedNameSet: Map<string> = {};
let nodeToGeneratedName: string[] = [];
let blockScopedVariableToGeneratedName: string[];
let computedPropertyNamesToGeneratedNames: string[];
let extendsEmitted = false;
@@ -268,6 +267,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
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);
@@ -648,19 +649,19 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
sourceMapDir = getDirectoryPath(normalizePath(jsFilePath));
}
function emitNodeWithSourceMap(node: Node, allowGeneratedIdentifiers?: boolean) {
function emitNodeWithSourceMap(node: Node) {
if (node) {
if (nodeIsSynthesized(node)) {
return emitNodeWithoutSourceMap(node, /*allowGeneratedIdentifiers*/ false);
return emitNodeWithoutSourceMap(node);
}
if (node.kind != SyntaxKind.SourceFile) {
recordEmitNodeStartSpan(node);
emitNodeWithoutSourceMap(node, allowGeneratedIdentifiers);
emitNodeWithoutSourceMap(node);
recordEmitNodeEndSpan(node);
}
else {
recordNewSourceFileStart(<SourceFile>node);
emitNodeWithoutSourceMap(node, /*allowGeneratedIdentifiers*/ false);
emitNodeWithoutSourceMap(node);
}
}
}
@@ -1187,6 +1188,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.ReturnStatement:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.SwitchStatement:
case SyntaxKind.TaggedTemplateExpression:
@@ -1235,52 +1237,54 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
}
else if (languageVersion < ScriptTarget.ES6) {
let declaration = resolver.getReferencedImportDeclaration(node);
if (declaration) {
if (declaration.kind === SyntaxKind.ImportClause) {
let alias = resolver.getReferencedImportDeclaration(node);
if (alias) {
if (alias.kind === SyntaxKind.ImportClause) {
// Identifier references default import
write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent));
write(getGeneratedNameForNode(<ImportDeclaration>alias.parent));
write(languageVersion === ScriptTarget.ES3 ? '["default"]' : ".default");
}
else {
// Identifier references named import
write(getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent));
write(getGeneratedNameForNode(<ImportDeclaration>alias.parent.parent.parent));
write(".");
writeTextOfNode(currentSourceFile, (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name);
writeTextOfNode(currentSourceFile, (<ImportSpecifier>alias).propertyName || (<ImportSpecifier>alias).name);
}
return;
}
let local = resolver.getReferencedNestedRedeclaration(node);
if (local) {
write(getGeneratedNameForNode(local.name));
return;
}
}
writeTextOfNode(currentSourceFile, node);
}
function getGeneratedNameForIdentifier(node: Identifier): string {
if (nodeIsSynthesized(node) || !blockScopedVariableToGeneratedName) {
return undefined;
}
var variableId = resolver.getBlockScopedVariableId(node)
if (variableId === undefined) {
return undefined;
}
return blockScopedVariableToGeneratedName[variableId];
}
function emitIdentifier(node: Identifier, allowGeneratedIdentifiers: boolean) {
if (allowGeneratedIdentifiers) {
let generatedName = getGeneratedNameForIdentifier(node);
if (generatedName) {
write(generatedName);
return;
function isNameOfNestedRedeclaration(node: Identifier) {
if (languageVersion < ScriptTarget.ES6) {
let parent = node.parent;
switch (parent.kind) {
case SyntaxKind.BindingElement:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.VariableDeclaration:
return (<Declaration>parent).name === node && resolver.isNestedRedeclaration(<Declaration>parent);
}
}
return false;
}
function emitIdentifier(node: Identifier) {
if (!node.parent) {
write(node.text);
}
else if (isExpressionIdentifier(node)) {
emitExpressionIdentifier(node);
}
else if (isNameOfNestedRedeclaration(node)) {
write(getGeneratedNameForNode(node));
}
else {
writeTextOfNode(currentSourceFile, node);
}
@@ -1326,7 +1330,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
function emitBindingElement(node: BindingElement) {
if (node.propertyName) {
emit(node.propertyName, /*allowGeneratedIdentifiers*/ false);
emit(node.propertyName);
write(": ");
}
if (node.dotDotDotToken) {
@@ -1679,7 +1683,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
write("*");
}
emit(node.name, /*allowGeneratedIdentifiers*/ false);
emit(node.name);
if (languageVersion < ScriptTarget.ES6) {
write(": function ");
}
@@ -1687,43 +1691,34 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
function emitPropertyAssignment(node: PropertyDeclaration) {
emit(node.name, /*allowGeneratedIdentifiers*/ false);
emit(node.name);
write(": ");
emit(node.initializer);
}
// Return true if identifier resolves to an exported member of a namespace
function isNamespaceExportReference(node: Identifier) {
let container = resolver.getReferencedExportContainer(node);
return container && container.kind !== SyntaxKind.SourceFile;
}
function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) {
emit(node.name, /*allowGeneratedIdentifiers*/ false);
// If short-hand property has a prefix, then regardless of the target version, we will emit it as normal property assignment. For example:
// module m {
// export let y;
// }
// module m {
// export let obj = { y };
// }
// The short-hand property in obj need to emit as such ... = { y : m.y } regardless of the TargetScript version
if (languageVersion < ScriptTarget.ES6) {
// The name property of a short-hand property assignment is considered an expression position, so here
// we manually emit the identifier to avoid rewriting.
writeTextOfNode(currentSourceFile, node.name);
// If emitting pre-ES6 code, or if the name requires rewriting when resolved as an expression identifier,
// we emit a normal property assignment. For example:
// module m {
// export let y;
// }
// module m {
// let obj = { y };
// }
// Here we need to emit obj = { y : m.y } regardless of the output target.
if (languageVersion < ScriptTarget.ES6 || isNamespaceExportReference(node.name)) {
// Emit identifier as an identifier
write(": ");
var generatedName = getGeneratedNameForIdentifier(node.name);
if (generatedName) {
write(generatedName);
}
else {
// Even though this is stored as identifier treat it as an expression
// Short-hand, { x }, is equivalent of normal form { x: x }
emitExpressionIdentifier(node.name);
}
}
else {
let container = resolver.getReferencedExportContainer(node.name);
if (container && container.kind !== SyntaxKind.SourceFile) {
// Emit identifier as an identifier
write(": ");
// Even though this is stored as identifier treat it as an expression
// Short-hand, { x }, is equivalent of normal form { x: x }
emitExpressionIdentifier(node.name);
}
emit(node.name);
}
}
@@ -1776,7 +1771,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
let indentedBeforeDot = indentIfOnDifferentLines(node, node.expression, node.dotToken);
write(".");
let indentedAfterDot = indentIfOnDifferentLines(node, node.dotToken, node.name);
emit(node.name, /*allowGeneratedIdentifiers*/ false);
emit(node.name);
decreaseIndentIf(indentedBeforeDot, indentedAfterDot);
}
@@ -2778,8 +2773,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
write(", ");
}
renameNonTopLevelLetAndConst(name);
const isVariableDeclarationOrBindingElement =
name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement);
@@ -2847,11 +2840,12 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
function createPropertyAccessForDestructuringProperty(object: Expression, propName: Identifier | LiteralExpression): Expression {
if (propName.kind !== SyntaxKind.Identifier) {
return createElementAccessExpression(object, propName);
let syntheticName = <Identifier | LiteralExpression>createSynthesizedNode(propName.kind);
syntheticName.text = propName.text;
if (syntheticName.kind !== SyntaxKind.Identifier) {
return createElementAccessExpression(object, syntheticName);
}
return createPropertyAccessExpression(object, propName);
return createPropertyAccessExpression(object, syntheticName);
}
function createSliceCall(value: Expression, sliceIndex: number): CallExpression {
@@ -2873,8 +2867,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
for (let p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
// TODO(andersh): Computed property support
let propName = <Identifier | LiteralExpression>((<PropertyAssignment>p).name);
let propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
emitDestructuringAssignment((<PropertyAssignment>p).initializer || propName, createPropertyAccessForDestructuringProperty(value, propName));
}
}
@@ -2988,8 +2981,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
}
else {
renameNonTopLevelLetAndConst(<Identifier>node.name);
let initializer = node.initializer;
if (!initializer && languageVersion < ScriptTarget.ES6) {
@@ -3049,54 +3040,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
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;
}
let 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
let list = getAncestor(node, SyntaxKind.VariableDeclarationList);
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);
let parent = blockScopeContainer.kind === SyntaxKind.SourceFile
? blockScopeContainer
: blockScopeContainer.parent;
if (resolver.resolvesToSomeValue(parent, (<Identifier>node).text)) {
let variableId = resolver.getBlockScopedVariableId(<Identifier>node);
if (!blockScopedVariableToGeneratedName) {
blockScopedVariableToGeneratedName = [];
}
let generatedName = makeUniqueName((<Identifier>node).text);
blockScopedVariableToGeneratedName[variableId] = generatedName;
}
}
function isES6ExportedDeclaration(node: Node) {
return !!(node.flags & NodeFlags.Export) &&
languageVersion >= ScriptTarget.ES6 &&
@@ -3260,7 +3203,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
function emitAccessor(node: AccessorDeclaration) {
write(node.kind === SyntaxKind.GetAccessor ? "get " : "set ");
emit(node.name, /*allowGeneratedIdentifiers*/ false);
emit(node.name);
emitSignatureAndBody(node);
}
@@ -5786,7 +5729,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
emitLeadingComments(node.endOfFileToken);
}
function emitNodeWithoutSourceMap(node: Node, allowGeneratedIdentifiers?: boolean): void {
function emitNodeWithoutSourceMap(node: Node): void {
if (!node) {
return;
}
@@ -5800,7 +5743,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
emitLeadingComments(node);
}
emitJavaScriptWorker(node, allowGeneratedIdentifiers);
emitJavaScriptWorker(node);
if (emitComments) {
emitTrailingComments(node);
@@ -5850,11 +5793,11 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
return true;
}
function emitJavaScriptWorker(node: Node, allowGeneratedIdentifiers: boolean = true) {
function emitJavaScriptWorker(node: Node) {
// Check if the node can be emitted regardless of the ScriptTarget
switch (node.kind) {
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>node, allowGeneratedIdentifiers);
return emitIdentifier(<Identifier>node);
case SyntaxKind.Parameter:
return emitParameter(<ParameterDeclaration>node);
case SyntaxKind.MethodDeclaration:
+5 -1
View File
@@ -1398,6 +1398,8 @@ module ts {
hasGlobalName(name: string): boolean;
getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration;
getReferencedImportDeclaration(node: Identifier): ImportClause | ImportSpecifier;
getReferencedNestedRedeclaration(node: Identifier): Declaration;
isNestedRedeclaration(node: Declaration): boolean;
isValueAliasDeclaration(node: Node): boolean;
isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean;
isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean;
@@ -1412,7 +1414,6 @@ module ts {
isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult;
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
resolvesToSomeValue(location: Node, name: string): boolean;
getBlockScopedVariableId(node: Identifier): number;
getReferencedValueDeclaration(reference: Identifier): Declaration;
serializeTypeOfNode(node: Node, getGeneratedNameForNode: (Node: Node) => string): string | string[];
@@ -1494,6 +1495,8 @@ module ts {
HasExports = Class | Enum | Module,
HasMembers = Class | Interface | TypeLiteral | ObjectLiteral,
BlockScoped = BlockScopedVariable | Class | Enum,
IsContainer = HasLocals | HasExports | HasMembers,
PropertyOrAccessor = Property | Accessor,
Export = ExportNamespace | ExportType | ExportValue,
@@ -1523,6 +1526,7 @@ module ts {
unionType?: UnionType; // Containing union type for union property
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
isNestedRedeclaration?: boolean; // True if symbol is block scoped redeclaration
}
/* @internal */