Fix handling of prologue statements when there are

parameter property declarations
If there is a prologue (and no super) in a constructor,
the prologue won't be skipped when calculating the
parameter properties, leading to the prologue being
included twice
This commit is contained in:
John Lusty
2022-04-20 01:47:47 +01:00
parent 2e619fdc80
commit 74c337bc5f
5 changed files with 155 additions and 6 deletions
+9 -6
View File
@@ -1294,7 +1294,7 @@ namespace ts {
resumeLexicalEnvironment();
const needsSyntheticConstructor = !constructor && isDerivedClass;
let indexOfFirstStatementAfterSuper = 0;
let indexOfFirstStatementAfterSuperAndPrologue = 0;
let prologueStatementCount = 0;
let superStatementIndex = -1;
let statements: Statement[] = [];
@@ -1305,13 +1305,16 @@ namespace ts {
// If there was a super call, visit existing statements up to and including it
if (superStatementIndex >= 0) {
indexOfFirstStatementAfterSuper = superStatementIndex + 1;
indexOfFirstStatementAfterSuperAndPrologue = superStatementIndex + 1;
statements = [
...statements.slice(0, prologueStatementCount),
...visitNodes(constructor.body.statements, visitor, isStatement, prologueStatementCount, indexOfFirstStatementAfterSuper - prologueStatementCount),
...visitNodes(constructor.body.statements, visitor, isStatement, prologueStatementCount, indexOfFirstStatementAfterSuperAndPrologue - prologueStatementCount),
...statements.slice(prologueStatementCount),
];
}
else if (prologueStatementCount >= 0) {
indexOfFirstStatementAfterSuperAndPrologue = prologueStatementCount;
}
}
if (needsSyntheticConstructor) {
@@ -1354,7 +1357,7 @@ namespace ts {
}
}
if (parameterPropertyDeclarationCount > 0) {
const parameterProperties = visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatementAfterSuper, parameterPropertyDeclarationCount);
const parameterProperties = visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatementAfterSuperAndPrologue, parameterPropertyDeclarationCount);
// If there was a super() call found, add parameter properties immediately after it
if (superStatementIndex >= 0) {
@@ -1373,7 +1376,7 @@ namespace ts {
statements = [...parameterProperties, ...statements];
}
indexOfFirstStatementAfterSuper += parameterPropertyDeclarationCount;
indexOfFirstStatementAfterSuperAndPrologue += parameterPropertyDeclarationCount;
}
}
}
@@ -1385,7 +1388,7 @@ namespace ts {
// Add existing statements after the initial prologues and super call
if (constructor) {
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuper + prologueStatementCount));
addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuperAndPrologue));
}
statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
@@ -0,0 +1,44 @@
//// [constructorWithParameterPropertiesAndPrivateFields.es2015.ts]
// https://github.com/microsoft/TypeScript/issues/48771
class A {
readonly #privateField: string;
constructor(arg: { key: string }, public exposedField: number) {
({ key: this.#privateField } = arg);
}
log() {
console.log(this.#privateField);
console.log(this.exposedField);
}
}
//// [constructorWithParameterPropertiesAndPrivateFields.es2015.js]
// https://github.com/microsoft/TypeScript/issues/48771
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _A_privateField;
class A {
constructor(arg, exposedField) {
this.exposedField = exposedField;
var _a;
_A_privateField.set(this, void 0);
(_a = this, { key: ({ set value(_b) { __classPrivateFieldSet(_a, _A_privateField, _b, "f"); } }).value } = arg);
}
log() {
console.log(__classPrivateFieldGet(this, _A_privateField, "f"));
console.log(this.exposedField);
}
}
_A_privateField = new WeakMap();
@@ -0,0 +1,41 @@
=== tests/cases/compiler/constructorWithParameterPropertiesAndPrivateFields.es2015.ts ===
// https://github.com/microsoft/TypeScript/issues/48771
class A {
>A : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
readonly #privateField: string;
>#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
constructor(arg: { key: string }, public exposedField: number) {
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 14))
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 20))
>exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
({ key: this.#privateField } = arg);
>key : Symbol(key, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 6, 6))
>this.#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
>arg : Symbol(arg, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 14))
}
log() {
>log : Symbol(A.log, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 7, 3))
console.log(this.#privateField);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this.#privateField : Symbol(A.#privateField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 2, 9))
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
console.log(this.exposedField);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this.exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
>this : Symbol(A, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 0, 0))
>exposedField : Symbol(A.exposedField, Decl(constructorWithParameterPropertiesAndPrivateFields.es2015.ts, 5, 35))
}
}
@@ -0,0 +1,46 @@
=== tests/cases/compiler/constructorWithParameterPropertiesAndPrivateFields.es2015.ts ===
// https://github.com/microsoft/TypeScript/issues/48771
class A {
>A : A
readonly #privateField: string;
>#privateField : string
constructor(arg: { key: string }, public exposedField: number) {
>arg : { key: string; }
>key : string
>exposedField : number
({ key: this.#privateField } = arg);
>({ key: this.#privateField } = arg) : { key: string; }
>{ key: this.#privateField } = arg : { key: string; }
>{ key: this.#privateField } : { key: string; }
>key : string
>this.#privateField : string
>this : this
>arg : { key: string; }
}
log() {
>log : () => void
console.log(this.#privateField);
>console.log(this.#privateField) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>this.#privateField : string
>this : this
console.log(this.exposedField);
>console.log(this.exposedField) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>this.exposedField : number
>this : this
>exposedField : number
}
}
@@ -0,0 +1,15 @@
// @target: es2015
// https://github.com/microsoft/TypeScript/issues/48771
class A {
readonly #privateField: string;
constructor(arg: { key: string }, public exposedField: number) {
({ key: this.#privateField } = arg);
}
log() {
console.log(this.#privateField);
console.log(this.exposedField);
}
}