From a6678847bbcf5bd4769cc931633fdcee29b0582b Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 16 Nov 2022 12:05:32 -0500 Subject: [PATCH] Avoid emitting temp variable in downleveled import() --- src/compiler/transformers/module/module.ts | 43 +++++++++++++++---- .../reference/asyncImportNestedYield.js | 5 +-- .../dynamicImportEvaluateSpecifier.js | 8 ++-- .../reference/dynamicImportTrailingComma.js | 3 +- .../importCallExpressionDeclarationEmit1.js | 12 +++--- .../importCallExpressionGrammarError.js | 5 +-- .../importCallExpressionNestedCJS.js | 3 +- .../importCallExpressionNestedCJS2.js | 3 +- .../importCallExpressionReturnPromiseOfAny.js | 18 ++++---- ...llExpressionSpecifierNotStringTypeError.js | 11 +++-- .../baselines/reference/jsdocInTypeScript.js | 3 +- 11 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index e5ade93e74fe9..9b4be50da8a23 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -850,32 +850,57 @@ export function transformModule(context: TransformationContext): (x: SourceFile function createImportCallExpressionCommonJS(arg: Expression | undefined, isInlineable?: boolean): Expression { // import(x) // emit as - // var _a; - // (_a = x, Promise.resolve().then(() => require(_a)) /*CommonJs Require*/ + // Promise.resolve(`${x}`).then((s) => require(s)) /*CommonJs Require*/ // We have to wrap require in then callback so that require is done in asynchronously // if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately - // If the arg is not inlineable, we have to evaluate it in the current scope with a temp var - const temp = arg && !isSimpleInlineableExpression(arg) && !isInlineable ? factory.createTempVariable(hoistVariableDeclaration) : undefined; + // If the arg is not inlineable, we have to evaluate and ToString() it in the current scope + // Otherwise, we inline it in require() so that it's statically analyzable + const needSyncEval = arg && !isSimpleInlineableExpression(arg) && !isInlineable; + const promiseResolveCall = factory.createCallExpression( factory.createPropertyAccessExpression(factory.createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, - /*argumentsArray*/ [], + /*argumentsArray*/ needSyncEval + ? languageVersion >= ScriptTarget.ES2015 + ? [ + factory.createTemplateExpression(factory.createTemplateHead(""), [ + factory.createTemplateSpan(arg, factory.createTemplateTail("")), + ]), + ] + : [ + factory.createCallExpression( + factory.createPropertyAccessExpression(factory.createStringLiteral(""), "concat"), + /*typeArguments*/ undefined, + [arg] + ), + ] + : [] ); + let requireCall: Expression = factory.createCallExpression( factory.createIdentifier("require"), /*typeArguments*/ undefined, - temp ? [temp] : arg ? [arg] : [], + needSyncEval ? [factory.createIdentifier("s")] : arg ? [arg] : [], ); if (getESModuleInterop(compilerOptions)) { requireCall = emitHelpers().createImportStarHelper(requireCall); } + const parameters = needSyncEval + ? [ + factory.createParameterDeclaration( + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + /*name*/ "s"), + ] + : []; + let func: FunctionExpression | ArrowFunction; if (languageVersion >= ScriptTarget.ES2015) { func = factory.createArrowFunction( /*modifiers*/ undefined, /*typeParameters*/ undefined, - /*parameters*/ [], + /*parameters*/ parameters, /*type*/ undefined, /*equalsGreaterThanToken*/ undefined, requireCall); @@ -886,14 +911,14 @@ export function transformModule(context: TransformationContext): (x: SourceFile /*asteriskToken*/ undefined, /*name*/ undefined, /*typeParameters*/ undefined, - /*parameters*/ [], + /*parameters*/ parameters, /*type*/ undefined, factory.createBlock([factory.createReturnStatement(requireCall)])); } const downleveledImport = factory.createCallExpression(factory.createPropertyAccessExpression(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]); - return temp === undefined ? downleveledImport : factory.createCommaListExpression([factory.createAssignment(temp, arg!), downleveledImport]); + return downleveledImport; } function getHelperExpressionForExport(node: ExportDeclaration, innerExpr: Expression) { diff --git a/tests/baselines/reference/asyncImportNestedYield.js b/tests/baselines/reference/asyncImportNestedYield.js index 190d930a1adcc..4aff42f45e146 100644 --- a/tests/baselines/reference/asyncImportNestedYield.js +++ b/tests/baselines/reference/asyncImportNestedYield.js @@ -46,13 +46,12 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar function foo() { return __asyncGenerator(this, arguments, function foo_1() { return __generator(this, function (_a) { - var _b, _c; switch (_a.label) { case 0: return [4 /*yield*/, __await("foo")]; case 1: return [4 /*yield*/, _a.sent()]; - case 2: return [4 /*yield*/, __await.apply(void 0, [(_b = _a.sent(), Promise.resolve().then(function () { return require(_b); }))])]; + case 2: return [4 /*yield*/, __await.apply(void 0, [Promise.resolve("".concat(_a.sent())).then(function (s) { return require(s); })])]; case 3: - _c = (_a.sent())["default"], Promise.resolve().then(function () { return require(_c); }); + Promise.resolve("".concat((_a.sent())["default"])).then(function (s) { return require(s); }); return [2 /*return*/]; } }); diff --git a/tests/baselines/reference/dynamicImportEvaluateSpecifier.js b/tests/baselines/reference/dynamicImportEvaluateSpecifier.js index 60c4a9f3d1f44..c696b2dd993d3 100644 --- a/tests/baselines/reference/dynamicImportEvaluateSpecifier.js +++ b/tests/baselines/reference/dynamicImportEvaluateSpecifier.js @@ -16,16 +16,14 @@ const someFunction = async () => { //// [dynamicImportEvaluateSpecifier.js] -var _a, _b; // https://github.com/microsoft/TypeScript/issues/48285 let i = 0; -_a = String(i++), Promise.resolve().then(() => require(_a)); -_b = String(i++), Promise.resolve().then(() => require(_b)); +Promise.resolve(`${String(i++)}`).then(s => require(s)); +Promise.resolve(`${String(i++)}`).then(s => require(s)); const getPath = async () => { /* in reality this would do some async FS operation, or a web request */ return "/root/my/cool/path"; }; const someFunction = async () => { - var _a; - const result = await (_a = await getPath(), Promise.resolve().then(() => require(_a))); + const result = await Promise.resolve(`${await getPath()}`).then(s => require(s)); }; diff --git a/tests/baselines/reference/dynamicImportTrailingComma.js b/tests/baselines/reference/dynamicImportTrailingComma.js index d28e9be57761d..c9c37af4dc54e 100644 --- a/tests/baselines/reference/dynamicImportTrailingComma.js +++ b/tests/baselines/reference/dynamicImportTrailingComma.js @@ -3,6 +3,5 @@ const path = './foo'; import(path,); //// [dynamicImportTrailingComma.js] -var _a; var path = './foo'; -_a = path, Promise.resolve().then(function () { return require(_a); }); +Promise.resolve("".concat(path)).then(function (s) { return require(s); }); diff --git a/tests/baselines/reference/importCallExpressionDeclarationEmit1.js b/tests/baselines/reference/importCallExpressionDeclarationEmit1.js index 89e42ee7f63de..5b6326ea2aa06 100644 --- a/tests/baselines/reference/importCallExpressionDeclarationEmit1.js +++ b/tests/baselines/reference/importCallExpressionDeclarationEmit1.js @@ -15,14 +15,12 @@ function returnDynamicLoad(path: string) { } //// [importCallExpressionDeclarationEmit1.js] -var _a, _b, _c, _d; -_a = getSpecifier(), Promise.resolve().then(() => require(_a)); -var p0 = (_b = `${directory}\\${moduleFile}`, Promise.resolve().then(() => require(_b))); -var p1 = (_c = getSpecifier(), Promise.resolve().then(() => require(_c))); -const p2 = (_d = whatToLoad ? getSpecifier() : "defaulPath", Promise.resolve().then(() => require(_d))); +Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +var p0 = Promise.resolve(`${`${directory}\\${moduleFile}`}`).then(s => require(s)); +var p1 = Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +const p2 = Promise.resolve(`${whatToLoad ? getSpecifier() : "defaulPath"}`).then(s => require(s)); function returnDynamicLoad(path) { - var _a; - return _a = path, Promise.resolve().then(() => require(_a)); + return Promise.resolve(`${path}`).then(s => require(s)); } diff --git a/tests/baselines/reference/importCallExpressionGrammarError.js b/tests/baselines/reference/importCallExpressionGrammarError.js index 0a502e5e2891a..0c25e3128f3fe 100644 --- a/tests/baselines/reference/importCallExpressionGrammarError.js +++ b/tests/baselines/reference/importCallExpressionGrammarError.js @@ -10,9 +10,8 @@ const p2 = import(); const p4 = import("pathToModule", "secondModule"); //// [importCallExpressionGrammarError.js] -var _a, _b; var a = ["./0"]; -_a = (...["PathModule"]), Promise.resolve().then(() => require(_a)); -var p1 = (_b = (...a), Promise.resolve().then(() => require(_b))); +Promise.resolve(`${...["PathModule"]}`).then(s => require(s)); +var p1 = Promise.resolve(`${...a}`).then(s => require(s)); const p2 = Promise.resolve().then(() => require()); const p4 = Promise.resolve().then(() => require("pathToModule")); diff --git a/tests/baselines/reference/importCallExpressionNestedCJS.js b/tests/baselines/reference/importCallExpressionNestedCJS.js index f099c9bc5fc53..3101057a49788 100644 --- a/tests/baselines/reference/importCallExpressionNestedCJS.js +++ b/tests/baselines/reference/importCallExpressionNestedCJS.js @@ -24,7 +24,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; function foo() { return __awaiter(this, void 0, void 0, function* () { - var _a; - return yield (_a = (yield Promise.resolve().then(() => require("./foo"))).default, Promise.resolve().then(() => require(_a))); + return yield Promise.resolve(`${(yield Promise.resolve().then(() => require("./foo"))).default}`).then(s => require(s)); }); } diff --git a/tests/baselines/reference/importCallExpressionNestedCJS2.js b/tests/baselines/reference/importCallExpressionNestedCJS2.js index a29cbe396f1c8..a40cf58f67480 100644 --- a/tests/baselines/reference/importCallExpressionNestedCJS2.js +++ b/tests/baselines/reference/importCallExpressionNestedCJS2.js @@ -52,10 +52,9 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function foo() { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { - var _b; switch (_a.label) { case 0: return [4 /*yield*/, Promise.resolve().then(function () { return require("./foo"); })]; - case 1: return [4 /*yield*/, (_b = (_a.sent()).default, Promise.resolve().then(function () { return require(_b); }))]; + case 1: return [4 /*yield*/, Promise.resolve("".concat((_a.sent()).default)).then(function (s) { return require(s); })]; case 2: return [2 /*return*/, _a.sent()]; } }); diff --git a/tests/baselines/reference/importCallExpressionReturnPromiseOfAny.js b/tests/baselines/reference/importCallExpressionReturnPromiseOfAny.js index b3d787c94a54e..0bda2486b75cc 100644 --- a/tests/baselines/reference/importCallExpressionReturnPromiseOfAny.js +++ b/tests/baselines/reference/importCallExpressionReturnPromiseOfAny.js @@ -42,23 +42,21 @@ class C { exports.C = C; //// [1.js] "use strict"; -var _a, _b, _c, _d, _e, _f, _g; Object.defineProperty(exports, "__esModule", { value: true }); -_a = `${directory}\\${moduleFile}`, Promise.resolve().then(() => require(_a)); -_b = getSpecifier(), Promise.resolve().then(() => require(_b)); -var p1 = (_c = ValidSomeCondition() ? "./0" : "externalModule", Promise.resolve().then(() => require(_c))); -var p1 = (_d = getSpecifier(), Promise.resolve().then(() => require(_d))); -var p11 = (_e = getSpecifier(), Promise.resolve().then(() => require(_e))); -const p2 = (_f = whatToLoad ? getSpecifier() : "defaulPath", Promise.resolve().then(() => require(_f))); +Promise.resolve(`${`${directory}\\${moduleFile}`}`).then(s => require(s)); +Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +var p1 = Promise.resolve(`${ValidSomeCondition() ? "./0" : "externalModule"}`).then(s => require(s)); +var p1 = Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +var p11 = Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +const p2 = Promise.resolve(`${whatToLoad ? getSpecifier() : "defaulPath"}`).then(s => require(s)); p1.then(zero => { return zero.foo(); // ok, zero is any }); let j; -var p3 = (_g = j = getSpecifier(), Promise.resolve().then(() => require(_g))); +var p3 = Promise.resolve(`${j = getSpecifier()}`).then(s => require(s)); function* loadModule(directories) { - var _a; for (const directory of directories) { const path = `${directory}\\moduleFile`; - _a = yield path, Promise.resolve().then(() => require(_a)); + Promise.resolve(`${yield path}`).then(s => require(s)); } } diff --git a/tests/baselines/reference/importCallExpressionSpecifierNotStringTypeError.js b/tests/baselines/reference/importCallExpressionSpecifierNotStringTypeError.js index ab2785a63d208..2191b285331c1 100644 --- a/tests/baselines/reference/importCallExpressionSpecifierNotStringTypeError.js +++ b/tests/baselines/reference/importCallExpressionSpecifierNotStringTypeError.js @@ -14,13 +14,12 @@ var p3 = import(["path1", "path2"]); var p4 = import(()=>"PathToModule"); //// [importCallExpressionSpecifierNotStringTypeError.js] -var _a, _b, _c, _d, _e; // Error specifier is not assignable to string -_a = getSpecifier(), Promise.resolve().then(() => require(_a)); -var p1 = (_b = getSpecifier(), Promise.resolve().then(() => require(_b))); -const p2 = (_c = whatToLoad ? getSpecifier() : "defaulPath", Promise.resolve().then(() => require(_c))); +Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +var p1 = Promise.resolve(`${getSpecifier()}`).then(s => require(s)); +const p2 = Promise.resolve(`${whatToLoad ? getSpecifier() : "defaulPath"}`).then(s => require(s)); p1.then(zero => { return zero.foo(); // ok, zero is any }); -var p3 = (_d = ["path1", "path2"], Promise.resolve().then(() => require(_d))); -var p4 = (_e = () => "PathToModule", Promise.resolve().then(() => require(_e))); +var p3 = Promise.resolve(`${["path1", "path2"]}`).then(s => require(s)); +var p4 = Promise.resolve(`${() => "PathToModule"}`).then(s => require(s)); diff --git a/tests/baselines/reference/jsdocInTypeScript.js b/tests/baselines/reference/jsdocInTypeScript.js index 39b8f917e0585..894c3162dd039 100644 --- a/tests/baselines/reference/jsdocInTypeScript.js +++ b/tests/baselines/reference/jsdocInTypeScript.js @@ -58,7 +58,6 @@ var v = import(String()); //// [jsdocInTypeScript.js] -var _a; var T = /** @class */ (function () { function T() { } @@ -93,4 +92,4 @@ var E = {}; E[""]; // make sure import types in JSDoc are not resolved /** @type {import("should-not-be-resolved").Type} */ -var v = (_a = String(), Promise.resolve().then(function () { return require(_a); })); +var v = Promise.resolve("".concat(String())).then(function (s) { return require(s); });