Skip to content

Commit 377b48f

Browse files
committed
Merge pull request #2948 from Microsoft/localStorageForExportedNames
Store exported names locally
2 parents 0d2e50b + 2184be8 commit 377b48f

File tree

7 files changed

+409
-45
lines changed

7 files changed

+409
-45
lines changed

src/compiler/emitter.ts

Lines changed: 157 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33

44
/* @internal */
55
module ts {
6-
// represents one LexicalEnvironment frame to store unique generated names
7-
interface ScopeFrame {
8-
names: Map<string>;
9-
previous: ScopeFrame;
10-
}
11-
126
export function isExternalModuleOrDeclarationFile(sourceFile: SourceFile) {
137
return isExternalModule(sourceFile) || isDeclarationFile(sourceFile);
148
}
@@ -18,7 +12,6 @@ module ts {
1812
Auto = 0x00000000, // No preferred name
1913
CountMask = 0x0FFFFFFF, // Temp variable counter
2014
_i = 0x10000000, // Use/preference flag for '_i'
21-
_n = 0x20000000, // Use/preference flag for '_n'
2215
}
2316

2417
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
@@ -2683,15 +2676,17 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
26832676
if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) {
26842677
for (let specifier of exportSpecifiers[name.text]) {
26852678
writeLine();
2686-
emitStart(specifier.name);
26872679
if (compilerOptions.module === ModuleKind.System) {
2680+
emitStart(specifier.name);
26882681
write(`${exportFunctionForFile}("`);
26892682
emitNodeWithoutSourceMap(specifier.name);
26902683
write(`", `);
26912684
emitExpressionIdentifier(name);
26922685
write(")")
2686+
emitEnd(specifier.name);
26932687
}
26942688
else {
2689+
emitStart(specifier.name);
26952690
emitContainingModuleName(specifier);
26962691
write(".");
26972692
emitNodeWithoutSourceMap(specifier.name);
@@ -4968,7 +4963,135 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
49684963
}
49694964
}
49704965

4971-
function hoistTopLevelVariableAndFunctionDeclarations(node: SourceFile): void {
4966+
function emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations: (Identifier | Declaration)[]): string {
4967+
// when resolving exports local exported entries/indirect exported entries in the module
4968+
// should always win over entries with similar names that were added via star exports
4969+
// to support this we store names of local/indirect exported entries in a set.
4970+
// this set is used to filter names brought by star expors.
4971+
if (!hasExportStars) {
4972+
// local names set is needed only in presence of star exports
4973+
return undefined;
4974+
}
4975+
4976+
// local names set should only be added if we have anything exported
4977+
if (!exportedDeclarations && isEmpty(exportSpecifiers)) {
4978+
// no exported declarations (export var ...) or export specifiers (export {x})
4979+
// check if we have any non star export declarations.
4980+
let hasExportDeclarationWithExportClause = false;
4981+
for (let externalImport of externalImports) {
4982+
if (externalImport.kind === SyntaxKind.ExportDeclaration && (<ExportDeclaration>externalImport).exportClause) {
4983+
hasExportDeclarationWithExportClause = true;
4984+
break;
4985+
}
4986+
}
4987+
4988+
if (!hasExportDeclarationWithExportClause) {
4989+
// we still need to emit exportStar helper
4990+
return emitExportStarFunction(/*localNames*/ undefined);
4991+
}
4992+
}
4993+
4994+
const exportedNamesStorageRef = makeUniqueName("exportedNames");
4995+
4996+
writeLine();
4997+
write(`var ${exportedNamesStorageRef} = {`);
4998+
increaseIndent();
4999+
5000+
let started = false;
5001+
if (exportedDeclarations) {
5002+
for (let i = 0; i < exportedDeclarations.length; ++i) {
5003+
// write name of exported declaration, i.e 'export var x...'
5004+
writeExportedName(exportedDeclarations[i]);
5005+
}
5006+
}
5007+
5008+
if (exportSpecifiers) {
5009+
for (let n in exportSpecifiers) {
5010+
for (let specifier of exportSpecifiers[n]) {
5011+
// write name of export specified, i.e. 'export {x}'
5012+
writeExportedName(specifier.name);
5013+
}
5014+
}
5015+
}
5016+
5017+
for (let externalImport of externalImports) {
5018+
if (externalImport.kind !== SyntaxKind.ExportDeclaration) {
5019+
continue;
5020+
}
5021+
5022+
let exportDecl = <ExportDeclaration>externalImport;
5023+
if (!exportDecl.exportClause) {
5024+
// export * from ...
5025+
continue;
5026+
}
5027+
5028+
for (let element of exportDecl.exportClause.elements) {
5029+
// write name of indirectly exported entry, i.e. 'export {x} from ...'
5030+
writeExportedName(element.name || element.propertyName);
5031+
}
5032+
}
5033+
5034+
decreaseIndent();
5035+
writeLine();
5036+
write("};");
5037+
5038+
return emitExportStarFunction(exportedNamesStorageRef);
5039+
5040+
function emitExportStarFunction(localNames: string): string {
5041+
const exportStarFunction = makeUniqueName("exportStar");
5042+
5043+
writeLine();
5044+
5045+
// define an export star helper function
5046+
write(`function ${exportStarFunction}(m) {`);
5047+
increaseIndent();
5048+
writeLine();
5049+
write(`for(var n in m) {`);
5050+
increaseIndent();
5051+
writeLine();
5052+
write(`if (n !== "default"`);
5053+
if (localNames) {
5054+
write(`&& !${localNames}.hasOwnProperty(n)`);
5055+
}
5056+
write(`) ${exportFunctionForFile}(n, m[n]);`);
5057+
decreaseIndent();
5058+
writeLine();
5059+
write("}");
5060+
decreaseIndent();
5061+
writeLine();
5062+
write("}")
5063+
5064+
return exportStarFunction;
5065+
}
5066+
5067+
function writeExportedName(node: Identifier | Declaration): void {
5068+
// do not record default exports
5069+
// they are local to module and never overwritten (explicitly skipped) by star export
5070+
if (node.kind !== SyntaxKind.Identifier && node.flags & NodeFlags.Default) {
5071+
return;
5072+
}
5073+
5074+
if (started) {
5075+
write(",");
5076+
}
5077+
else {
5078+
started = true;
5079+
}
5080+
5081+
writeLine();
5082+
write("'");
5083+
if (node.kind === SyntaxKind.Identifier) {
5084+
emitNodeWithoutSourceMap(node);
5085+
}
5086+
else {
5087+
emitDeclarationName(<Declaration>node);
5088+
}
5089+
5090+
write("': true");
5091+
}
5092+
}
5093+
5094+
function processTopLevelVariableAndFunctionDeclarations(node: SourceFile): (Identifier | Declaration)[] {
49725095
// per ES6 spec:
49735096
// 15.2.1.16.4 ModuleDeclarationInstantiation() Concrete Method
49745097
// - var declarations are initialized to undefined - 14.a.ii
@@ -4980,6 +5103,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
49805103
// including variables declarations for module and class declarations
49815104
let hoistedVars: (Identifier | ClassDeclaration | ModuleDeclaration)[];
49825105
let hoistedFunctionDeclarations: FunctionDeclaration[];
5106+
let exportedDeclarations: (Identifier | Declaration)[];
49835107

49845108
visit(node);
49855109

@@ -4997,6 +5121,14 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
49975121
else {
49985122
emit(local);
49995123
}
5124+
5125+
let flags = getCombinedNodeFlags(local.kind === SyntaxKind.Identifier ? local.parent : local);
5126+
if (flags & NodeFlags.Export) {
5127+
if (!exportedDeclarations) {
5128+
exportedDeclarations = [];
5129+
}
5130+
exportedDeclarations.push(local);
5131+
}
50005132
}
50015133
write(";")
50025134
}
@@ -5005,9 +5137,18 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
50055137
for (let f of hoistedFunctionDeclarations) {
50065138
writeLine();
50075139
emit(f);
5140+
5141+
if (f.flags & NodeFlags.Export) {
5142+
if (!exportedDeclarations) {
5143+
exportedDeclarations = [];
5144+
}
5145+
exportedDeclarations.push(f);
5146+
}
50085147
}
50095148
}
50105149

5150+
return exportedDeclarations;
5151+
50115152
function visit(node: Node): void {
50125153
if (node.kind === SyntaxKind.FunctionDeclaration) {
50135154
if (!hoistedFunctionDeclarations) {
@@ -5121,12 +5262,13 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
51215262
// }
51225263
emitVariableDeclarationsForImports();
51235264
writeLine();
5124-
hoistTopLevelVariableAndFunctionDeclarations(node);
5265+
var exportedDeclarations = processTopLevelVariableAndFunctionDeclarations(node);
5266+
let exportStarFunction = emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations)
51255267
writeLine();
51265268
write("return {");
51275269
increaseIndent();
51285270
writeLine();
5129-
emitSetters();
5271+
emitSetters(exportStarFunction);
51305272
writeLine();
51315273
emitExecute(node, startIndex);
51325274
emitTempDeclarations(/*newLine*/ true)
@@ -5135,7 +5277,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
51355277
write("}"); // return
51365278
}
51375279

5138-
function emitSetters() {
5280+
function emitSetters(exportStarFunction: string) {
51395281
write("setters:[");
51405282
for (let i = 0; i < externalImports.length; ++i) {
51415283
if (i !== 0) {
@@ -5163,7 +5305,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
51635305
increaseIndent();
51645306
writeLine();
51655307
// save import into the local
5166-
write(`${importVariableName} = ${parameterName}`);
5308+
write(`${importVariableName} = ${parameterName};`);
51675309
writeLine();
51685310

51695311
let defaultName =
@@ -5228,9 +5370,8 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) {
52285370
writeLine();
52295371
// export * from 'foo'
52305372
// emit as:
5231-
// for (var n in _foo) exports(n, _foo[n]);
5232-
// NOTE: it is safe to use name 'n' since parameter name always starts with '_'
5233-
write(`for (var n in ${parameterName}) ${exportFunctionForFile}(n, ${parameterName}[n]);`);
5373+
// exportStar(_foo);
5374+
write(`${exportStarFunction}(${parameterName});`);
52345375
}
52355376

52365377
writeLine();

tests/baselines/reference/systemModule10.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ System.register(['file1', 'file2'], function(exports_1) {
1515
return {
1616
setters:[
1717
function (_file1_1) {
18-
file1_1 = _file1_1
18+
file1_1 = _file1_1;
1919
exports_1("n", file1_1["default"]);
2020
exports_1("n1", file1_1["default"]);
2121
exports_1("x", file1_1.x);
2222
exports_1("y", file1_1.x);
2323
},
2424
function (_n2) {
25-
n2 = _n2
25+
n2 = _n2;
2626
exports_1("n2", n2);
2727
exports_1("n3", n2);
2828
}],

tests/baselines/reference/systemModule10_ES5.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ System.register(['file1', 'file2'], function(exports_1) {
1515
return {
1616
setters:[
1717
function (_file1_1) {
18-
file1_1 = _file1_1
18+
file1_1 = _file1_1;
1919
exports_1("n", file1_1.default);
2020
exports_1("n1", file1_1.default);
2121
exports_1("x", file1_1.x);
2222
exports_1("y", file1_1.x);
2323
},
2424
function (_n2) {
25-
n2 = _n2
25+
n2 = _n2;
2626
exports_1("n2", n2);
2727
exports_1("n3", n2);
2828
}],
Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,59 @@
1-
tests/cases/compiler/systemModule11.ts(3,17): error TS2307: Cannot find module 'bar'.
1+
tests/cases/compiler/file1.ts(7,15): error TS2307: Cannot find module 'bar'.
2+
tests/cases/compiler/file2.ts(7,15): error TS2307: Cannot find module 'bar'.
3+
tests/cases/compiler/file3.ts(2,25): error TS2307: Cannot find module 'a'.
4+
tests/cases/compiler/file3.ts(4,15): error TS2307: Cannot find module 'bar'.
5+
tests/cases/compiler/file4.ts(9,27): error TS2307: Cannot find module 'a'.
6+
tests/cases/compiler/file5.ts(3,15): error TS2307: Cannot find module 'a'.
27

38

4-
==== tests/cases/compiler/systemModule11.ts (1 errors) ====
9+
==== tests/cases/compiler/file1.ts (1 errors) ====
510

6-
import 'foo'
7-
import {f} from 'bar';
8-
~~~~~
11+
// set of tests cases that checks generation of local storage for exported names
12+
13+
14+
export var x;
15+
export function foo() {}
16+
export * from 'bar';
17+
~~~~~
18+
!!! error TS2307: Cannot find module 'bar'.
19+
20+
==== tests/cases/compiler/file2.ts (1 errors) ====
21+
22+
var x;
23+
var y;
24+
export {x};
25+
export {y as y1}
26+
27+
export * from 'bar';
28+
~~~~~
29+
!!! error TS2307: Cannot find module 'bar'.
30+
31+
==== tests/cases/compiler/file3.ts (2 errors) ====
32+
33+
export {x, y as z} from 'a';
34+
~~~
35+
!!! error TS2307: Cannot find module 'a'.
36+
export default function foo() {}
37+
export * from 'bar';
38+
~~~~~
939
!!! error TS2307: Cannot find module 'bar'.
1040

11-
f();
41+
==== tests/cases/compiler/file4.ts (1 errors) ====
42+
43+
export var x;
44+
export function foo() {}
45+
export default function (){}
46+
47+
var z, z1;
48+
export {z, z1 as z2};
49+
50+
export {s, s1 as s2} from 'a'
51+
~~~
52+
!!! error TS2307: Cannot find module 'a'.
53+
54+
==== tests/cases/compiler/file5.ts (1 errors) ====
55+
56+
function foo() {}
57+
export * from 'a';
58+
~~~
59+
!!! error TS2307: Cannot find module 'a'.

0 commit comments

Comments
 (0)