Support for 'export *' declarations

This commit is contained in:
Anders Hejlsberg
2015-02-15 08:25:24 -08:00
parent c60121064a
commit a8152b6e50
4 changed files with 105 additions and 28 deletions

View File

@@ -316,6 +316,13 @@ module ts {
}
}
function bindExportDeclaration(node: ExportDeclaration) {
if (!node.exportClause) {
((<ExportContainer>container).exportStars || ((<ExportContainer>container).exportStars = [])).push(node);
}
bindChildren(node, 0, /*isBlockScopeContainer*/ false);
}
function bindFunctionOrConstructorType(node: SignatureDeclaration) {
// For a given function symbol "<...>(...) => T" we want to generate a symbol identical
// to the one we would get for: { <...>(...): T }
@@ -477,6 +484,9 @@ module ts {
case SyntaxKind.ExportSpecifier:
bindDeclaration(<Declaration>node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.ExportDeclaration:
bindExportDeclaration(<ExportDeclaration>node);
break;
case SyntaxKind.ImportClause:
if ((<ImportClause>node).name) {
bindDeclaration(<Declaration>node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false);

View File

@@ -179,7 +179,7 @@ module ts {
return result;
}
function extendSymbol(target: Symbol, source: Symbol) {
function mergeSymbol(target: Symbol, source: Symbol) {
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
// reset flag when merging instantiated module into value module that has only const enums
@@ -192,11 +192,11 @@ module ts {
});
if (source.members) {
if (!target.members) target.members = {};
extendSymbolTable(target.members, source.members);
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
if (!target.exports) target.exports = {};
extendSymbolTable(target.exports, source.exports);
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
}
@@ -222,7 +222,7 @@ module ts {
return result;
}
function extendSymbolTable(target: SymbolTable, source: SymbolTable) {
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
for (var id in source) {
if (hasProperty(source, id)) {
if (!hasProperty(target, id)) {
@@ -233,12 +233,20 @@ module ts {
if (!(symbol.flags & SymbolFlags.Merged)) {
target[id] = symbol = cloneSymbol(symbol);
}
extendSymbol(symbol, source[id]);
mergeSymbol(symbol, source[id]);
}
}
}
}
function extendSymbolTable(target: SymbolTable, source: SymbolTable) {
for (var id in source) {
if (!hasProperty(target, id)) {
target[id] = source[id];
}
}
}
function getSymbolLinks(symbol: Symbol): SymbolLinks {
if (symbol.flags & SymbolFlags.Transient) return <TransientSymbol>symbol;
if (!symbol.id) symbol.id = nextSymbolId++;
@@ -486,7 +494,7 @@ module ts {
if (moduleSymbol) {
var name = specifier.propertyName || specifier.name;
if (name.text) {
var symbol = getSymbol(moduleSymbol.exports, name.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
var symbol = getSymbol(getExportsOfSymbol(moduleSymbol), name.text, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
if (!symbol) {
error(name, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(moduleSymbol), declarationNameToString(name));
return;
@@ -587,7 +595,7 @@ module ts {
else if (name.kind === SyntaxKind.QualifiedName) {
var namespace = resolveEntityName(location,(<QualifiedName>name).left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || getFullWidth((<QualifiedName>name).right) === 0) return;
var symbol = getSymbol(namespace.exports,(<QualifiedName>name).right.text, meaning);
var symbol = getSymbol(getExportsOfSymbol(namespace), (<QualifiedName>name).right.text, meaning);
if (!symbol) {
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
declarationNameToString((<QualifiedName>name).right));
@@ -708,6 +716,41 @@ module ts {
};
}
function getExportsOfSymbol(symbol: Symbol): SymbolTable {
return symbol.flags & SymbolFlags.Module ? getExportsOfModule(symbol) : symbol.exports;
}
function getExportsOfModule(symbol: Symbol): SymbolTable {
var links = getSymbolLinks(symbol);
return links.resolvedExports || (links.resolvedExports = getExportsForModule(symbol));
}
function getExportsForModule(symbol: Symbol): SymbolTable {
var result: SymbolTable;
var visitedSymbols: Symbol[] = [];
visit(symbol);
return result;
function visit(symbol: Symbol) {
if (!contains(visitedSymbols, symbol)) {
visitedSymbols.push(symbol);
if (!result) {
result = symbol.exports;
}
else {
extendSymbolTable(result, symbol.exports);
}
forEach(symbol.declarations, node => {
if (node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.ModuleDeclaration) {
forEach((<ExportContainer>node).exportStars, exportStar => {
visit(resolveExternalModuleName(exportStar, exportStar.moduleSpecifier));
});
}
});
}
}
}
function getMergedSymbol(symbol: Symbol): Symbol {
var merged: Symbol;
return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
@@ -2475,7 +2518,7 @@ module ts {
var callSignatures: Signature[] = emptyArray;
var constructSignatures: Signature[] = emptyArray;
if (symbol.flags & SymbolFlags.HasExports) {
members = symbol.exports;
members = getExportsOfSymbol(symbol);
}
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) {
callSignatures = getSignaturesOfSymbol(symbol);
@@ -10468,7 +10511,7 @@ module ts {
// Initialize global symbol table
forEach(host.getSourceFiles(), file => {
if (!isExternalModule(file)) {
extendSymbolTable(globals, file.locals);
mergeSymbolTable(globals, file.locals);
}
});

View File

@@ -3057,11 +3057,15 @@ module ts {
return <ModuleDeclaration>node;
}
function emitContainingModuleName(node: Node) {
var container = getContainingModule(node);
write(container ? resolver.getGeneratedNameForNode(container) : "exports");
}
function emitModuleMemberName(node: Declaration) {
emitStart(node.name);
if (getCombinedNodeFlags(node) & NodeFlags.Export) {
var container = getContainingModule(node);
write(container ? resolver.getGeneratedNameForNode(container) : "exports");
emitContainingModuleName(node);
write(".");
}
emitNode(node.name);
@@ -3073,7 +3077,8 @@ module ts {
forEach(exportSpecifiers[name.text], specifier => {
writeLine();
emitStart(specifier.name);
write("exports.");
emitContainingModuleName(specifier);
write(".");
emitNode(specifier.name);
emitEnd(specifier.name);
write(" = ");
@@ -4117,27 +4122,41 @@ module ts {
}
function emitExportDeclaration(node: ExportDeclaration) {
if (node.exportClause && node.moduleSpecifier) {
var generatedName = resolver.getGeneratedNameForNode(node);
if (node.moduleSpecifier) {
emitStart(node);
var generatedName = resolver.getGeneratedNameForNode(node);
if (compilerOptions.module !== ModuleKind.AMD) {
write("var ");
write(generatedName);
write(" = ");
emitRequire(getExternalModuleName(node));
}
forEach(node.exportClause.elements, specifier => {
if (node.exportClause) {
// export { x, y, ... }
forEach(node.exportClause.elements, specifier => {
writeLine();
emitStart(specifier);
emitContainingModuleName(specifier);
write(".");
emitNode(specifier.name);
write(" = ");
write(generatedName);
write(".");
emitNode(specifier.propertyName || specifier.name);
write(";");
emitEnd(specifier);
});
}
else {
// export *
var tempName = createTempVariable(node).text;
writeLine();
emitStart(specifier);
write("exports.");
emitNode(specifier.name);
write(" = ");
write(generatedName);
write(".");
emitNode(specifier.propertyName || specifier.name);
write(";");
emitEnd(specifier);
});
write("for (var " + tempName + " in " + generatedName + ") if (!");
emitContainingModuleName(node);
write(".hasOwnProperty(" + tempName + ")) ");
emitContainingModuleName(node);
write("[" + tempName + "] = " + generatedName + "[" + tempName + "];");
}
emitEnd(node);
}
}

View File

@@ -352,13 +352,13 @@ module ts {
// Specific context the parser was in when this node was created. Normally undefined.
// Only set when the parser was in some interesting context (like async/yield).
parserContextFlags?: ParserContextFlags;
modifiers?: ModifiersArray; // Array of modifiers
id?: number; // Unique id (used to look up NodeLinks)
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)
nextContainer?: Node; // Next container in declaration order (initialized by binding)
localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
modifiers?: ModifiersArray; // Array of modifiers
}
export interface NodeArray<T> extends Array<T>, TextRange {
@@ -856,7 +856,11 @@ module ts {
members: NodeArray<EnumMember>;
}
export interface ModuleDeclaration extends Declaration, ModuleElement {
export interface ExportContainer {
exportStars?: ExportDeclaration[]; // List of 'export *' statements (initialized by binding)
}
export interface ModuleDeclaration extends Declaration, ModuleElement, ExportContainer {
name: Identifier | LiteralExpression;
body: ModuleBlock | ModuleDeclaration;
}
@@ -934,7 +938,7 @@ module ts {
}
// Source files are declarations when they are external modules.
export interface SourceFile extends Declaration {
export interface SourceFile extends Declaration, ExportContainer {
statements: NodeArray<ModuleElement>;
endOfFileToken: Node;
@@ -1297,6 +1301,7 @@ module ts {
exportAssignmentChecked?: boolean; // True if export assignment was checked
exportAssignmentSymbol?: Symbol; // Symbol exported from external module
unionType?: UnionType; // Containing union type for union property
resolvedExports?: SymbolTable; // Resolved exports of module
}
export interface TransientSymbol extends Symbol, SymbolLinks { }