Ensuring local module names are unique in emit.

Fixes #41 and #42.
This commit is contained in:
Anders Hejlsberg
2014-07-18 17:04:06 -07:00
committed by unknown
parent 5d8773aa25
commit 85225c8f29
34 changed files with 264 additions and 209 deletions

View File

@@ -32,6 +32,7 @@ module ts {
var parent: Node;
var container: Declaration;
var symbolCount = 0;
var lastLocals: Declaration;
var Symbol = objectAllocator.getSymbolConstructor();
if (!file.locals) {
@@ -162,6 +163,14 @@ module ts {
}
}
// All nodes with locals are kept on a linked list in declaration order. This list is used by the getLocalNameOfContainer function
// in the type checker to validate that the local name used for a container is unique.
function initializeLocals(node: Declaration) {
node.locals = {};
if (lastLocals) lastLocals.nextLocals = node;
lastLocals = node;
}
function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags) {
switch (container.kind) {
case SyntaxKind.ModuleDeclaration:
@@ -198,7 +207,7 @@ module ts {
declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes);
break;
}
if (symbolKind & SymbolFlags.HasLocals) node.locals = {};
if (symbolKind & SymbolFlags.HasLocals) initializeLocals(node);
var saveParent = parent;
var saveContainer = container;
parent = node;
@@ -232,7 +241,7 @@ module ts {
function bindAnonymousDeclaration(node: Node, symbolKind: SymbolFlags, name: string) {
var symbol = createSymbol(symbolKind, name);
addDeclarationToSymbol(symbol, node, symbolKind);
if (symbolKind & SymbolFlags.HasLocals) node.locals = {};
if (symbolKind & SymbolFlags.HasLocals) initializeLocals(node);
var saveParent = parent;
var saveContainer = container;
parent = node;

View File

@@ -3795,7 +3795,7 @@ module ts {
}
return checkUntypedCall(node);
}
// If FuncExprs apparent type(section 3.8.1) is a function type, the call is a typed function call.
// If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call.
// TypeScript employs overload resolution in typed function calls in order to support functions
// with multiple call signatures.
if (!signatures.length) {
@@ -3827,7 +3827,7 @@ module ts {
return checkUntypedCall(node);
}
// If ConstructExprs apparent type(section 3.8.1) is an object type with one or
// If ConstructExpr's apparent type(section 3.8.1) is an object type with one or
// more construct signatures, the expression is processed in the same manner as a
// function call, but using the construct signatures as the initial set of candidate
// signatures for overload resolution.The result type of the function call becomes
@@ -3848,7 +3848,7 @@ module ts {
return checkCall(node, constructSignatures);
}
// If ConstructExprs apparent type is an object type with no construct signatures but
// If ConstructExpr's apparent type is an object type with no construct signatures but
// one or more call signatures, the expression is processed as a function call. A compile-time
// error occurs if the result of the function call is not Void. The type of the result of the
// operation is Any.
@@ -6004,14 +6004,53 @@ module ts {
// Emitter support
function getModuleObjectName(node: ModuleDeclaration): string {
return getSourceTextOfNode(node.name);
}
function isExternalModuleSymbol(symbol: Symbol): boolean {
return symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length === 1 && symbol.declarations[0].kind === SyntaxKind.SourceFile;
}
function isNodeParentedBy(node: Node, parent: Node): boolean {
while (node) {
if (node === parent) return true;
node = node.parent;
}
return false;
}
function isUniqueLocalName(name: string, container: Node): boolean {
name = escapeIdentifier(name);
if (container.locals) {
for (var node = container; isNodeParentedBy(node, container); node = node.nextLocals) {
if (hasProperty(node.locals, name) && node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue)) {
return false;
}
}
}
return true;
}
function getLocalNameOfContainer(container: Declaration): string {
var links = getNodeLinks(container);
if (!links.localModuleName) {
var name = container.name.text ? unescapeIdentifier(container.name.text) : "M";
while (!isUniqueLocalName(name, container)) {
name = "_" + name;
}
links.localModuleName = name;
}
return links.localModuleName;
}
function getLocalNameForSymbol(symbol: Symbol, location: Node): string {
var node = location;
while (node) {
if ((node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(node) === symbol) {
return getLocalNameOfContainer(node);
}
node = node.parent;
}
Debug.fail("getLocalNameForSymbol failed");
}
function getExpressionNamePrefix(node: Identifier): string {
var symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
@@ -6021,15 +6060,11 @@ module ts {
// kind of entity. SymbolFlags.ExportHasLocal encompasses all the kinds that we
// do NOT prefix.
var exportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
var isExportedSymbolFoundInLocalScope = exportSymbol !== symbol;
var shouldEmitExportWithoutPrefix = (exportSymbol.flags & SymbolFlags.ExportHasLocal) !== 0;
if (isExportedSymbolFoundInLocalScope && !shouldEmitExportWithoutPrefix) {
if (symbol !== exportSymbol && !(exportSymbol.flags & SymbolFlags.ExportHasLocal)) {
symbol = exportSymbol;
}
// symbol will have a parent if it is an export symbol
if (symbol.parent) {
return isExternalModuleSymbol(symbol.parent) ? "exports" : symbolToString(symbol.parent);
return isExternalModuleSymbol(symbol.parent) ? "exports" : getLocalNameForSymbol(getParentOfSymbol(symbol), node.parent);
}
}
}
@@ -6116,7 +6151,7 @@ module ts {
function invokeEmitter() {
var resolver: EmitResolver = {
getProgram: () => program,
getModuleObjectName: getModuleObjectName,
getLocalNameOfContainer: getLocalNameOfContainer,
getExpressionNamePrefix: getExpressionNamePrefix,
getPropertyAccessSubstitution: getPropertyAccessSubstitution,
getExportAssignmentName: getExportAssignmentName,

View File

@@ -1043,7 +1043,7 @@ module ts {
emitStart(node.name);
if (node.flags & NodeFlags.Export) {
var container = getContainingModule(node);
write(container ? resolver.getModuleObjectName(container) : "exports");
write(container ? resolver.getLocalNameOfContainer(container) : "exports");
write(".");
}
emitNode(node.name);
@@ -1444,16 +1444,18 @@ module ts {
writeLine();
emitStart(node);
write("(function (");
emit(node.name);
emitStart(node.name);
write(resolver.getLocalNameOfContainer(node));
emitEnd(node.name);
write(") {");
increaseIndent();
scopeEmitStart(node);
forEach(node.members, member => {
writeLine();
emitStart(member);
emitNode(node.name);
write(resolver.getLocalNameOfContainer(node));
write("[");
emitNode(node.name);
write(resolver.getLocalNameOfContainer(node));
write("[");
emitQuotedIdentifier(member.name);
write("] = ");
@@ -1509,7 +1511,9 @@ module ts {
}
emitStart(node);
write("(function (");
emit(node.name);
emitStart(node.name);
write(resolver.getLocalNameOfContainer(node));
emitEnd(node.name);
write(") ");
if (node.body.kind === SyntaxKind.ModuleBlock) {
emit(node.body);

View File

@@ -62,7 +62,12 @@ module ts {
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
export function escapeIdentifier(identifier: string): string {
return identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier;
return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier;
}
// Remove extra underscore from escaped identifier
export function unescapeIdentifier(identifier: string): string {
return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier;
}
// Return display name of an identifier

View File

@@ -142,18 +142,18 @@ module ts {
As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers
IdentifierStart ::
Can contain Unicode 3.0.0 categories:
Uppercase letter (Lu),
Lowercase letter (Ll),
Titlecase letter (Lt),
Modifier letter (Lm),
Other letter (Lo), or
Letter number (Nl).
Uppercase letter (Lu),
Lowercase letter (Ll),
Titlecase letter (Lt),
Modifier letter (Lm),
Other letter (Lo), or
Letter number (Nl).
IdentifierPart :: =
Can contain IdentifierStart + Unicode 3.0.0 categories:
Non-spacing mark (Mn),
Combining spacing mark (Mc),
Decimal number (Nd), or
Connector punctuation (Pc).
Non-spacing mark (Mn),
Combining spacing mark (Mc),
Decimal number (Nd), or
Connector punctuation (Pc).
Codepoint ranges for ES3 Identifiers are extracted from the Unicode 3.0.0 specification at:
http://www.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.txt
@@ -165,18 +165,18 @@ module ts {
As per ECMAScript Language Specification 5th Edition, Section 7.6: ISyntaxToken Names and Identifiers
IdentifierStart ::
Can contain Unicode 6.2 categories:
Uppercase letter (Lu),
Lowercase letter (Ll),
Titlecase letter (Lt),
Modifier letter (Lm),
Other letter (Lo), or
Letter number (Nl).
Uppercase letter (Lu),
Lowercase letter (Ll),
Titlecase letter (Lt),
Modifier letter (Lm),
Other letter (Lo), or
Letter number (Nl).
IdentifierPart ::
Can contain IdentifierStart + Unicode 6.2 categories:
Non-spacing mark (Mn),
Combining spacing mark (Mc),
Decimal number (Nd),
Connector punctuation (Pc),
Non-spacing mark (Mn),
Combining spacing mark (Mc),
Decimal number (Nd),
Connector punctuation (Pc),
<ZWNJ>, or
<ZWJ>.

View File

@@ -240,6 +240,7 @@ module ts {
parent?: Node; // Parent node (initialized by binding)
symbol?: Symbol; // Symbol declared by node (initialized by binding)
locals?: SymbolTable; // Locals associated with node (initialized by binding)
nextLocals?: Node; // Next node in declaration order with locals (initialized by binding)
}
export interface NodeArray<T> extends Array<T>, TextRange { }
@@ -614,7 +615,7 @@ module ts {
export interface EmitResolver {
getProgram(): Program;
getModuleObjectName(node: ModuleDeclaration): string;
getLocalNameOfContainer(container: Declaration): string;
getExpressionNamePrefix(node: Identifier): string;
getPropertyAccessSubstitution(node: PropertyAccess): string;
getExportAssignmentName(node: SourceFile): string;
@@ -690,7 +691,7 @@ module ts {
ExportHasLocal = Function | Class | Enum | ValueModule,
HasLocals = Function | Module | Method | Constructor | Accessor | Signature,
HasLocals = Function | Enum | Module | Method | Constructor | Accessor | Signature,
HasExports = Class | Enum | Module,
HasMembers = Class | Interface | TypeLiteral | ObjectLiteral,
@@ -742,7 +743,8 @@ module ts {
flags?: NodeCheckFlags; // Set of flags specific to Node
enumMemberValue?: number; // Constant value of enum member
isIllegalTypeReferenceInConstraint?: boolean; // Is type reference in constraint refers to the type parameter from the same list
isVisible?: boolean; // Is this node visible
isVisible?: boolean; // Is this node visible
localModuleName?: string; // Local name for module instance
}
export enum TypeFlags {