From e79ce386fc4ab8e83372fa694971283a19a11a67 Mon Sep 17 00:00:00 2001 From: JoostK Date: Mon, 16 Nov 2020 17:56:36 +0100 Subject: [PATCH] refactor(compiler-cli): allow visiting call expressions without an active linker (#39707) The compilation result of components may have inserted template functions into the constant pool, which would be inserted into the Babel AST upon program exit. Babel will then proceed with visiting this newly inserted subtree, but we have already cleaned up the linker instance when exiting the program. Any call expressions within the template functions would then fail to be processed, as a file linker would no longer be available. Since the inserted AST subtree is known not to contain yet more partial declarations, it is safe to skip visiting call expressions when no file linker is available. PR Close #39707 --- .../linker/babel/src/es2015_linker_plugin.ts | 9 +++-- .../babel/test/es2015_linker_plugin_spec.ts | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts b/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts index b13be8e407..0ebe7b5db5 100644 --- a/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts +++ b/packages/compiler-cli/linker/babel/src/es2015_linker_plugin.ts @@ -58,9 +58,14 @@ export function createEs2015LinkerPlugin(options: Partial = {}): * with the results of linking the declaration. */ CallExpression(call: NodePath): void { - try { - assertNotNull(fileLinker); + if (fileLinker === null) { + // Any statements that are inserted upon program exit will be visited outside of an active + // linker context. These call expressions are known not to contain partial declarations, + // so it's safe to skip visiting those call expressions. + return; + } + try { const callee = call.node.callee; if (!t.isExpression(callee)) { return; diff --git a/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts b/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts index 556010a192..da4b2bc8b8 100644 --- a/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts +++ b/packages/compiler-cli/linker/babel/test/es2015_linker_plugin_spec.ts @@ -213,6 +213,41 @@ describe('createEs2015LinkerPlugin()', () => { `(function(){const x1=[1];return function BAR(){};})();BAR;`, ].join('')); }); + + it('should not process call expressions within inserted functions', () => { + spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration') + .and.callFake(((sourceUrl, code, constantPool) => { + // Insert a call expression into the constant pool. This is inserted into + // Babel's AST upon program exit, and will therefore be visited by Babel + // outside of an active linker context. + constantPool.statements.push( + o.fn(/* params */[], /* body */[], /* type */ undefined, + /* sourceSpan */ undefined, /* name */ 'inserted') + .callFn([]) + .toStmt()); + + return o.literal('REPLACEMENT'); + }) as typeof PartialDirectiveLinkerVersion1.prototype.linkPartialDeclaration); + + const isPartialDeclarationSpy = + spyOn(FileLinker.prototype, 'isPartialDeclaration').and.callThrough(); + + const result = transformSync( + [ + 'import * as core from \'some-module\';', + `ɵɵngDeclareDirective({version: 1, ngImport: core})`, + ].join('\n'), + { + plugins: [createEs2015LinkerPlugin()], + filename: '/test.js', + parserOpts: {sourceType: 'unambiguous'}, + generatorOpts: {compact: true}, + }); + expect(result!.code) + .toEqual('import*as core from\'some-module\';(function inserted(){})();"REPLACEMENT";'); + + expect(isPartialDeclarationSpy.calls.allArgs()).toEqual([['ɵɵngDeclareDirective']]); + }); }); /**