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
This commit is contained in:
JoostK 2021-03-21 19:10:36 +01:00 committed by Joey Perrott
parent 8d3da56eda
commit 274dc15452
3 changed files with 50 additions and 8 deletions

View File

@ -34,15 +34,22 @@ import {GetterFn, MethodFn, SetterFn} from './types';
* var _this = _super.apply(this, arguments) || this; * var _this = _super.apply(this, arguments) || this;
* ``` * ```
* *
* downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2:
* ``` * ```
* function MyClass() { * function MyClass() {
* var _this = _super.apply(this, __spread(arguments)) || this; * 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. * More details can be found in: https://github.com/angular/angular/issues/38453.
*/ */
export const ES5_DELEGATE_CTOR = 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. */ /** 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+[^{]+{/; export const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
/** /**

View File

@ -27,7 +27,7 @@ genrule(
$(execpath @npm//typescript/bin:tsc) $< --outDir $$es2015_out_dir --target ES2015 \ $(execpath @npm//typescript/bin:tsc) $< --outDir $$es2015_out_dir --target ES2015 \
--types --module umd --types --module umd
$(execpath @npm//typescript/bin:tsc) --outFile $@ $$es2015_out_file --allowJs \ $(execpath @npm//typescript/bin:tsc) --outFile $@ $$es2015_out_file --allowJs \
--types --target ES5 --types --target ES5 --downlevelIteration
""", """,
tools = ["@npm//typescript/bin:tsc"], tools = ["@npm//typescript/bin:tsc"],
) )

View File

@ -202,13 +202,48 @@ class TestObj {
}); });
// See: https://github.com/angular/angular/issues/38453 // See: https://github.com/angular/angular/issues/38453
it('should support ES2015 downleveled classes', () => { it('should support ES2015 downleveled classes (workspace TypeScript version) (downlevelIteration=true)',
const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} = () => {
require('./es5_downleveled_inheritance_fixture'); const {ChildNoCtor, ChildNoCtorPrivateProps, ChildWithCtor} =
require('./es5_downleveled_inheritance_fixture');
expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true); expect(isDelegateCtor(ChildNoCtor.toString())).toBe(true);
expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true); expect(isDelegateCtor(ChildNoCtorPrivateProps.toString())).toBe(true);
expect(isDelegateCtor(ChildWithCtor.toString())).toBe(false); expect(isDelegateCtor(ChildWithCtor.toString())).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, __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', () => { it('should support ES2015 classes when minified', () => {