From 71d28acf0cfd5271a7d34f2a60d44a7526eb6375 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 16 Jan 2022 21:16:53 +0100 Subject: [PATCH] fix(compiler): properly compile DI factories when coverage reporting is enabled (#44732) When running tests with code coverage using Istanbul, the code is instrumented with coverage reporting statements. These statements are also inserted into synthesized constructors, preventing Angular from properly recognizing them as synthesized constructor. This commit changes the regex to detect synthesized constructors to allow for statements within the constructor before the `super(...arguments);` call. This is limited to code that does not contain a `}`, but this is sufficient to support Istanbul's coverage instrumentation statements. The tests have been extended with an input file that is being instrumented using `babel-plugin-istanbul` for both ES2015 and ES5 targets, in order to verify that the approach works for real-world usages. Fixes #31337 PR Close #44732 --- .../src/reflection/reflection_capabilities.ts | 2 +- .../core/test/reflection/reflector_spec.ts | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index 744f3053810..b2e1ae694e7 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -63,7 +63,7 @@ export const ES2015_INHERITED_CLASS_WITH_CTOR = * and inherit a constructor. */ export const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = - /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s*super\(\.\.\.arguments\)/; + /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/; /** * Determine whether a stringified type is a class which delegates its constructor diff --git a/packages/core/test/reflection/reflector_spec.ts b/packages/core/test/reflection/reflector_spec.ts index 556182c4cd4..9af4fd30b90 100644 --- a/packages/core/test/reflection/reflector_spec.ts +++ b/packages/core/test/reflection/reflector_spec.ts @@ -292,6 +292,51 @@ class TestObj { expect(isDelegateCtor(ChildWithCtorComplexBase)).toBe(false); }); + it('should deal with coverage instrumentation statements', () => { + // The Istanbul coverage tool augments code with additional statements to capture coverage + // information, and this test verifies that those do not interfere with our ability to + // detect delegate constructors. + const Es2015ClassNoCtor = `class TestService extends Parent { + constructor() { + cov_8nt6qq5zt().f[2]++; + cov_8nt6qq5zt().s[6]++; + super(...arguments); + cov_8nt6qq5zt().s[7]++; + this.foo = 'bar'; + } + }`; + const Es2015ClassWithCtor = `class TestService extends Parent { + constructor() { + cov_8nt6qq5zt().f[2]++; + cov_8nt6qq5zt().s[6]++; + super(); + cov_8nt6qq5zt().s[7]++; + this.foo = 'bar'; + } + }`; + const Es5ClassNoCtor = `function TestService() { + cov_8nt6qq5zt().f[4]++; + var _this = (cov_8nt6qq5zt().s[8]++, (cov_8nt6qq5zt().b[0][0]++, _super !== null) && (cov_8nt6qq5zt().b[0][1]++, _super.apply(this, arguments)) || (cov_8nt6qq5zt().b[0][2]++, this)); + cov_8nt6qq5zt().s[9]++; + _this.foo = 'bar'; + cov_8nt6qq5zt().s[10]++; + return _this; + }`; + const Es5ClassWithCtor = `function TestService() { + cov_8nt6qq5zt().f[4]++; + var _this = _super.call(); + cov_8nt6qq5zt().s[9]++; + _this.foo = 'bar'; + cov_8nt6qq5zt().s[10]++; + return _this; + }`; + + expect(isDelegateCtor(Es2015ClassNoCtor)).toBe(true); + expect(isDelegateCtor(Es2015ClassWithCtor)).toBe(false); + expect(isDelegateCtor(Es5ClassNoCtor)).toBe(true); + expect(isDelegateCtor(Es5ClassWithCtor)).toBe(false); + }); + it('should properly handle all class forms', () => { const ctor = (str: string) => expect(isDelegateCtor(str)).toBe(false); const noCtor = (str: string) => expect(isDelegateCtor(str)).toBe(true); @@ -299,6 +344,7 @@ class TestObj { ctor(`class Bar extends Foo {constructor(){}}`); ctor(`class Bar extends Foo { constructor ( ) {} }`); ctor(`class Bar extends Foo { other(){}; constructor(){} }`); + ctor(`class Bar extends Foo { constructor(){} a(){super(...arguments)} }`); noCtor(`class extends Foo{}`); noCtor(`class extends Foo {}`);