From 274dc15452739e4fab2f647804a64d5b797cfed5 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 21 Mar 2021 19:10:36 +0100 Subject: [PATCH] fix(core): detect synthesized constructors that have been downleveled using TS 4.2 (#41305) TypeScript 4.2 has changed its emitted syntax for synthetic constructors when using `downlevelIteration`, which affects ES5 bundles that have been downleveled from ES2015 bundles. This is typically the case for UMD bundles in the APF spec, as they are generated by downleveling the ESM2015 bundle into ES5. The reflection capabilities in the runtime need to recognize this new form to correctly deal with synthesized constructors, as otherwise JIT compilation could generate invalid factory functions. Fixes #41298 PR Close #41305 --- .../src/reflection/reflection_capabilities.ts | 9 +++- packages/core/test/BUILD.bazel | 2 +- .../core/test/reflection/reflector_spec.ts | 47 ++++++++++++++++--- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/packages/core/src/reflection/reflection_capabilities.ts b/packages/core/src/reflection/reflection_capabilities.ts index 75575ebb13..c4ad36405b 100644 --- a/packages/core/src/reflection/reflection_capabilities.ts +++ b/packages/core/src/reflection/reflection_capabilities.ts @@ -34,15 +34,22 @@ import {GetterFn, MethodFn, SetterFn} from './types'; * var _this = _super.apply(this, arguments) || this; * ``` * + * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2: * ``` * function MyClass() { * var _this = _super.apply(this, __spread(arguments)) || this; * ``` * + * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2: + * ``` + * function MyClass() { + * var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + * ``` + * * More details can be found in: https://github.com/angular/angular/issues/38453. */ export const ES5_DELEGATE_CTOR = - /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|[^()]+\(arguments\))\)/; + /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\))\)/; /** Regular expression that detects ES2015 classes which extend from other classes. */ export const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; /** diff --git a/packages/core/test/BUILD.bazel b/packages/core/test/BUILD.bazel index 8827492936..746631ca27 100644 --- a/packages/core/test/BUILD.bazel +++ b/packages/core/test/BUILD.bazel @@ -27,7 +27,7 @@ genrule( $(execpath @npm//typescript/bin:tsc) $< --outDir $$es2015_out_dir --target ES2015 \ --types --module umd $(execpath @npm//typescript/bin:tsc) --outFile $@ $$es2015_out_file --allowJs \ - --types --target ES5 + --types --target ES5 --downlevelIteration """, tools = ["@npm//typescript/bin:tsc"], ) diff --git a/packages/core/test/reflection/reflector_spec.ts b/packages/core/test/reflection/reflector_spec.ts index 87123b7087..137edb7f88 100644 --- a/packages/core/test/reflection/reflector_spec.ts +++ b/packages/core/test/reflection/reflector_spec.ts @@ -202,13 +202,48 @@ class TestObj { }); // See: https://github.com/angular/angular/issues/38453 - it('should support ES2015 downleveled classes', () => { - const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = - require('./es5_downleveled_inheritance_fixture'); + it('should support ES2015 downleveled classes (workspace TypeScript version) (downlevelIteration=true)', + () => { + const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = + require('./es5_downleveled_inheritance_fixture'); - expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); - expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); - expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); + expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); + expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); + }); + + it('should support ES2015 downleveled classes ( { + const ChildNoCtor = `function ChildNoCtor() { + return _super !== null && _super.apply(this, arguments) || this; + }`; + const ChildNoCtorPrivateProps = `function ChildNoCtorPrivateProps() { + var _this = _super.apply(this, __spread(arguments)) || this; + _this.x = 10; + return _this; + }`; + const ChildWithCtor = `function ChildWithCtor() { + return _super.call(this) || this; + }`; + expect(isDelegateCtor(ChildNoCtor)).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps)).toBe(true); + expect(isDelegateCtor(ChildWithCtor)).toBe(false); + }); + + it('should support ES2015 downleveled classes (>=TS4.2) (downlevelIteration=true)', () => { + const ChildNoCtor = `function ChildNoCtor() { + return _super !== null && _super.apply(this, arguments) || this; + }`; + const ChildNoCtorPrivateProps = `function ChildNoCtorPrivateProps() { + var _this = _super.apply(this, __spreadArray([], __read(arguments))) || this; + _this.x = 10; + return _this; + }`; + const ChildWithCtor = `function ChildWithCtor() { + return _super.call(this) || this; + }`; + expect(isDelegateCtor(ChildNoCtor)).toBe(true); + expect(isDelegateCtor(ChildNoCtorPrivateProps)).toBe(true); + expect(isDelegateCtor(ChildWithCtor)).toBe(false); }); it('should support ES2015 classes when minified', () => {