From aa1ad5233e84dfe23c0978ee4070c940e2bcee05 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 28 Apr 2015 16:18:33 -0700 Subject: [PATCH] generate local storage for all exported names to avoid overwriting them via star exports --- src/compiler/emitter.ts | 150 ++++++++++++++++-- tests/baselines/reference/systemModule10.js | 4 +- .../baselines/reference/systemModule10_ES5.js | 4 +- .../reference/systemModule11.errors.txt | 50 +++++- tests/baselines/reference/systemModule11.js | 116 ++++++++++++-- tests/baselines/reference/systemModule9.js | 17 +- tests/cases/compiler/systemModule11.ts | 33 +++- 7 files changed, 329 insertions(+), 45 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 48718146ac6..b32c5f9f210 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3,12 +3,6 @@ /* @internal */ module ts { - // represents one LexicalEnvironment frame to store unique generated names - interface ScopeFrame { - names: Map; - previous: ScopeFrame; - } - export function isExternalModuleOrDeclarationFile(sourceFile: SourceFile) { return isExternalModule(sourceFile) || isDeclarationFile(sourceFile); } @@ -18,7 +12,6 @@ module ts { Auto = 0x00000000, // No preferred name CountMask = 0x0FFFFFFF, // Temp variable counter _i = 0x10000000, // Use/preference flag for '_i' - _n = 0x20000000, // Use/preference flag for '_n' } // 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) { if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { for (let specifier of exportSpecifiers[name.text]) { writeLine(); - emitStart(specifier.name); if (compilerOptions.module === ModuleKind.System) { + emitStart(specifier.name); write(`${exportFunctionForFile}("`); emitNodeWithoutSourceMap(specifier.name); write(`", `); emitExpressionIdentifier(name); write(")") + emitEnd(specifier.name); } else { + emitStart(specifier.name); emitContainingModuleName(specifier); write("."); emitNodeWithoutSourceMap(specifier.name); @@ -4968,7 +4963,108 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { } } - function hoistTopLevelVariableAndFunctionDeclarations(node: SourceFile): void { + function emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations: (Identifier | Declaration)[]): string { + // when resolving exports local exported entries/indirect exported entries in the module + // should always win over entries with similar names that were added via star exports + // to support this we store names of local/indirect exported entries in a set. + // this set is used to filter names brought by star expors. + + if (!hasExportStars) { + // local names set is needed only in presence of star exports + return undefined; + } + + // local names set should only be added if we have anything exported + if (!exportedDeclarations && isEmpty(exportSpecifiers)) { + // no exported declarations (export var ...) or export specifiers (export {x}) + // check if we have any non star export declarations. + let hasExportDeclarationWithExportClause = false; + for (let externalImport of externalImports) { + if (externalImport.kind === SyntaxKind.ExportDeclaration && (externalImport).exportClause) { + hasExportDeclarationWithExportClause = true; + break; + } + } + + if (!hasExportDeclarationWithExportClause) { + // nothing is exported + return undefined; + } + } + + const exportedNamesStorageRef = makeUniqueName("exportedNames"); + + writeLine(); + write(`var ${exportedNamesStorageRef} = { `); + increaseIndent(); + + let started = false; + if (exportedDeclarations) { + for (let i = 0; i < exportedDeclarations.length; ++i) { + // write name of exported declaration, i.e 'export var x...' + writeExportedName(exportedDeclarations[i]); + } + } + + if (exportSpecifiers) { + for (let n in exportSpecifiers) { + for (let specifier of exportSpecifiers[n]) { + // write name of export specified, i.e. 'export {x}' + writeExportedName(specifier.name); + } + } + } + + if (externalImports) { + for (let externalImport of externalImports) { + if (externalImport.kind !== SyntaxKind.ExportDeclaration) { + continue; + } + + let exportDecl = externalImport; + if (!exportDecl.exportClause) { + // export * from ... + continue; + } + + for (let element of exportDecl.exportClause.elements) { + // write name of indirectly exported entry, i.e. 'export {x} from ...' + writeExportedName(element.name || element.propertyName); + } + } + } + + decreaseIndent(); + writeLine(); + write("};"); + + return exportedNamesStorageRef; + + function writeExportedName(node: Identifier | Declaration): void { + if (started) { + write(","); + } + else { + started = true; + } + + writeLine(); + write("'"); + if (node.kind === SyntaxKind.Identifier) { + emitNodeWithoutSourceMap(node); + } + else if (node.flags & NodeFlags.Default) { + write("default"); + } + else { + emitDeclarationName(node); + } + + write("': true"); + } + } + + function processTopLevelVariableAndFunctionDeclarations(node: SourceFile): (Identifier | Declaration)[] { // per ES6 spec: // 15.2.1.16.4 ModuleDeclarationInstantiation() Concrete Method // - var declarations are initialized to undefined - 14.a.ii @@ -4980,6 +5076,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { // including variables declarations for module and class declarations let hoistedVars: (Identifier | ClassDeclaration | ModuleDeclaration)[]; let hoistedFunctionDeclarations: FunctionDeclaration[]; + let exportedDeclarations: (Identifier | Declaration)[]; visit(node); @@ -4997,6 +5094,14 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { else { emit(local); } + + let flags = getCombinedNodeFlags(local.kind === SyntaxKind.Identifier ? local.parent : local); + if (flags & NodeFlags.Export) { + if (!exportedDeclarations) { + exportedDeclarations = []; + } + exportedDeclarations.push(local); + } } write(";") } @@ -5005,9 +5110,18 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { for (let f of hoistedFunctionDeclarations) { writeLine(); emit(f); + + if (f.flags & NodeFlags.Export) { + if (!exportedDeclarations) { + exportedDeclarations = []; + } + exportedDeclarations.push(f); + } } } + return exportedDeclarations; + function visit(node: Node): void { if (node.kind === SyntaxKind.FunctionDeclaration) { if (!hoistedFunctionDeclarations) { @@ -5121,12 +5235,13 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { // } emitVariableDeclarationsForImports(); writeLine(); - hoistTopLevelVariableAndFunctionDeclarations(node); + var exportedDeclarations = processTopLevelVariableAndFunctionDeclarations(node); + let exportedLocalNames = emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations) writeLine(); write("return {"); increaseIndent(); writeLine(); - emitSetters(); + emitSetters(exportedLocalNames); writeLine(); emitExecute(node, startIndex); emitTempDeclarations(/*newLine*/ true) @@ -5135,7 +5250,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { write("}"); // return } - function emitSetters() { + function emitSetters(exportedLocalNamesRef: string) { write("setters:["); for (let i = 0; i < externalImports.length; ++i) { if (i !== 0) { @@ -5163,7 +5278,7 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { increaseIndent(); writeLine(); // save import into the local - write(`${importVariableName} = ${parameterName}`); + write(`${importVariableName} = ${parameterName};`); writeLine(); let defaultName = @@ -5230,7 +5345,14 @@ if (typeof __param !== "function") __param = function (paramIndex, decorator) { // emit as: // for (var n in _foo) exports(n, _foo[n]); // NOTE: it is safe to use name 'n' since parameter name always starts with '_' - write(`for (var n in ${parameterName}) ${exportFunctionForFile}(n, ${parameterName}[n]);`); + write(`for (var n in ${parameterName})`); + increaseIndent(); + writeLine(); + if (exportedLocalNamesRef) { + write(`if (!${exportedLocalNamesRef}.hasOwnProperty(n))`); + } + write(` ${exportFunctionForFile}(n, ${parameterName}[n]);`) + decreaseIndent(); } writeLine(); diff --git a/tests/baselines/reference/systemModule10.js b/tests/baselines/reference/systemModule10.js index 66e7e9755d1..c63b3589085 100644 --- a/tests/baselines/reference/systemModule10.js +++ b/tests/baselines/reference/systemModule10.js @@ -15,14 +15,14 @@ System.register(['file1', 'file2'], function(exports_1) { return { setters:[ function (_file1_1) { - file1_1 = _file1_1 + file1_1 = _file1_1; exports_1("n", file1_1["default"]); exports_1("n1", file1_1["default"]); exports_1("x", file1_1.x); exports_1("y", file1_1.x); }, function (_n2) { - n2 = _n2 + n2 = _n2; exports_1("n2", n2); exports_1("n3", n2); }], diff --git a/tests/baselines/reference/systemModule10_ES5.js b/tests/baselines/reference/systemModule10_ES5.js index 04c946d9abe..bdb920414de 100644 --- a/tests/baselines/reference/systemModule10_ES5.js +++ b/tests/baselines/reference/systemModule10_ES5.js @@ -15,14 +15,14 @@ System.register(['file1', 'file2'], function(exports_1) { return { setters:[ function (_file1_1) { - file1_1 = _file1_1 + file1_1 = _file1_1; exports_1("n", file1_1.default); exports_1("n1", file1_1.default); exports_1("x", file1_1.x); exports_1("y", file1_1.x); }, function (_n2) { - n2 = _n2 + n2 = _n2; exports_1("n2", n2); exports_1("n3", n2); }], diff --git a/tests/baselines/reference/systemModule11.errors.txt b/tests/baselines/reference/systemModule11.errors.txt index a2c30e5fa64..2ea3e97daf9 100644 --- a/tests/baselines/reference/systemModule11.errors.txt +++ b/tests/baselines/reference/systemModule11.errors.txt @@ -1,11 +1,49 @@ -tests/cases/compiler/systemModule11.ts(3,17): error TS2307: Cannot find external module 'bar'. +tests/cases/compiler/file1.ts(7,15): error TS2307: Cannot find external module 'bar'. +tests/cases/compiler/file2.ts(7,15): error TS2307: Cannot find external module 'bar'. +tests/cases/compiler/file3.ts(2,25): error TS2307: Cannot find external module 'a'. +tests/cases/compiler/file3.ts(3,15): error TS2307: Cannot find external module 'bar'. +tests/cases/compiler/file4.ts(8,27): error TS2307: Cannot find external module 'a'. -==== tests/cases/compiler/systemModule11.ts (1 errors) ==== +==== tests/cases/compiler/file1.ts (1 errors) ==== - import 'foo' - import {f} from 'bar'; - ~~~~~ + // set of tests cases that checks generation of local storage for exported names + + + export var x; + export function foo() {} + export * from 'bar'; + ~~~~~ !!! error TS2307: Cannot find external module 'bar'. - f(); \ No newline at end of file +==== tests/cases/compiler/file2.ts (1 errors) ==== + + var x; + var y; + export {x}; + export {y as y1} + + export * from 'bar'; + ~~~~~ +!!! error TS2307: Cannot find external module 'bar'. + +==== tests/cases/compiler/file3.ts (2 errors) ==== + + export {x, y as z} from 'a'; + ~~~ +!!! error TS2307: Cannot find external module 'a'. + export * from 'bar'; + ~~~~~ +!!! error TS2307: Cannot find external module 'bar'. + +==== tests/cases/compiler/file4.ts (1 errors) ==== + + export var x; + export function foo() {} + + var z, z1; + export {z, z1 as z2}; + + export {s, s1 as s2} from 'a' + ~~~ +!!! error TS2307: Cannot find external module 'a'. \ No newline at end of file diff --git a/tests/baselines/reference/systemModule11.js b/tests/baselines/reference/systemModule11.js index f04cabcf935..85e275ebbcf 100644 --- a/tests/baselines/reference/systemModule11.js +++ b/tests/baselines/reference/systemModule11.js @@ -1,21 +1,113 @@ -//// [systemModule11.ts] - -import 'foo' -import {f} from 'bar'; - -f(); +//// [tests/cases/compiler/systemModule11.ts] //// -//// [systemModule11.js] -System.register(['foo', 'bar'], function(exports_1) { - var bar_1; +//// [file1.ts] + +// set of tests cases that checks generation of local storage for exported names + + +export var x; +export function foo() {} +export * from 'bar'; + +//// [file2.ts] + +var x; +var y; +export {x}; +export {y as y1} + +export * from 'bar'; + +//// [file3.ts] + +export {x, y as z} from 'a'; +export * from 'bar'; + +//// [file4.ts] + +export var x; +export function foo() {} + +var z, z1; +export {z, z1 as z2}; + +export {s, s1 as s2} from 'a' + +//// [file1.js] +// set of tests cases that checks generation of local storage for exported names +System.register(['bar'], function(exports_1) { + var x; + function foo() { } + exports_1("foo", foo); + var exportedNames_1 = { + 'x': true, + 'foo': true + }; return { setters:[ - function (_) {}, function (_bar_1) { - bar_1 = _bar_1 + for (var n in _bar_1) + if (!exportedNames_1.hasOwnProperty(n)) exports_1(n, _bar_1[n]); }], execute: function() { - bar_1.f(); + exports_1("x", x); + } + } +}); +//// [file2.js] +System.register(['bar'], function(exports_1) { + var x, y; + var exportedNames_1 = { + 'x': true, + 'y1': true + }; + return { + setters:[ + function (_bar_1) { + for (var n in _bar_1) + if (!exportedNames_1.hasOwnProperty(n)) exports_1(n, _bar_1[n]); + }], + execute: function() { + exports_1("x", x); + exports_1("y1", y); + } + } +}); +//// [file3.js] +System.register(['a', 'bar'], function(exports_1) { + var exportedNames_1 = { + 'x': true, + 'z': true + }; + return { + setters:[ + function (_a_1) { + exports_1("x", _a_1["x"]); + exports_1("z", _a_1["y"]); + }, + function (_bar_1) { + for (var n in _bar_1) + if (!exportedNames_1.hasOwnProperty(n)) exports_1(n, _bar_1[n]); + }], + execute: function() { + } + } +}); +//// [file4.js] +System.register(['a'], function(exports_1) { + var x, z, z1; + function foo() { } + exports_1("foo", foo); + return { + setters:[ + function (_a_1) { + exports_1("s", _a_1["s"]); + exports_1("s2", _a_1["s1"]); + }], + execute: function() { + exports_1("x", x); + exports_1("z", z); + exports_1("z2", z1); } } }); diff --git a/tests/baselines/reference/systemModule9.js b/tests/baselines/reference/systemModule9.js index 831aa778f38..55f7707ecb1 100644 --- a/tests/baselines/reference/systemModule9.js +++ b/tests/baselines/reference/systemModule9.js @@ -25,26 +25,31 @@ export {y as z}; System.register(['file1', 'file2', 'file3', 'file4', 'file5', 'file6', 'file7'], function(exports_1) { var ns, file2_1, file3_1, file5_1, ns3; var x, y; + var exportedNames_1 = { + 'x': true, + 'z': true + }; return { setters:[ function (_ns) { - ns = _ns + ns = _ns; }, function (_file2_1) { - file2_1 = _file2_1 + file2_1 = _file2_1; }, function (_file3_1) { - file3_1 = _file3_1 + file3_1 = _file3_1; }, function (_) {}, function (_file5_1) { - file5_1 = _file5_1 + file5_1 = _file5_1; }, function (_ns3) { - ns3 = _ns3 + ns3 = _ns3; }, function (_file7_1) { - for (var n in _file7_1) exports_1(n, _file7_1[n]); + for (var n in _file7_1) + if (!exportedNames_1.hasOwnProperty(n)) exports_1(n, _file7_1[n]); }], execute: function() { ns.f(); diff --git a/tests/cases/compiler/systemModule11.ts b/tests/cases/compiler/systemModule11.ts index 023b4f5f56b..9e27cc919ab 100644 --- a/tests/cases/compiler/systemModule11.ts +++ b/tests/cases/compiler/systemModule11.ts @@ -1,7 +1,34 @@ // @module: system // @separateCompilation: true -import 'foo' -import {f} from 'bar'; +// set of tests cases that checks generation of local storage for exported names -f(); \ No newline at end of file +// @filename: file1.ts + +export var x; +export function foo() {} +export * from 'bar'; + +// @filename: file2.ts + +var x; +var y; +export {x}; +export {y as y1} + +export * from 'bar'; + +// @filename: file3.ts + +export {x, y as z} from 'a'; +export * from 'bar'; + +// @filename: file4.ts + +export var x; +export function foo() {} + +var z, z1; +export {z, z1 as z2}; + +export {s, s1 as s2} from 'a' \ No newline at end of file