Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2331,7 +2331,7 @@ namespace ts {
propagateChildFlags(node.expression) |
(isIdentifier(node.name) ?
propagateIdentifierNameFlags(node.name) :
propagateChildFlags(node.name));
propagateChildFlags(node.name) | TransformFlags.ContainsPrivateIdentifierInExpression);
if (isSuperKeyword(expression)) {
// super method calls require a lexical 'this'
// super method calls require 'super' hoisting in ES2017 and ES2018 async functions and async generators
Expand Down Expand Up @@ -2366,7 +2366,7 @@ namespace ts {
propagateChildFlags(node.questionDotToken) |
(isIdentifier(node.name) ?
propagateIdentifierNameFlags(node.name) :
propagateChildFlags(node.name));
propagateChildFlags(node.name) | TransformFlags.ContainsPrivateIdentifierInExpression);
return node;
}

Expand Down Expand Up @@ -2851,6 +2851,9 @@ namespace ts {
else if (isLogicalOrCoalescingAssignmentOperator(operatorKind)) {
node.transformFlags |= TransformFlags.ContainsES2021;
}
if (operatorKind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) {
node.transformFlags |= TransformFlags.ContainsPrivateIdentifierInExpression;
}
return node;
}

Expand Down
90 changes: 70 additions & 20 deletions src/compiler/transformers/legacyDecorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,52 +68,88 @@ namespace ts {
function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> {
if (!(classOrConstructorParameterIsDecorated(node) || childIsDecorated(node))) return visitEachChild(node, visitor, context);

const classStatement = hasDecorators(node) ?
createClassDeclarationHeadWithDecorators(node, node.name) :
createClassDeclarationHeadWithoutDecorators(node, node.name);

const statements: Statement[] = [classStatement];

// Write any decorators of the node.
addClassElementDecorationStatements(statements, node, /*isStatic*/ false);
addClassElementDecorationStatements(statements, node, /*isStatic*/ true);
addConstructorDecorationStatement(statements, node);
const statements = hasDecorators(node) ?
transformClassDeclarationWithClassDecorators(node, node.name) :
transformClassDeclarationWithoutClassDecorators(node, node.name);

if (statements.length > 1) {
// Add a DeclarationMarker as a marker for the end of the declaration
statements.push(factory.createEndOfDeclarationMarker(node));
setEmitFlags(classStatement, getEmitFlags(classStatement) | EmitFlags.HasEndOfDeclarationMarker);
setEmitFlags(statements[0], getEmitFlags(statements[0]) | EmitFlags.HasEndOfDeclarationMarker);
}

return singleOrMany(statements);
}

function decoratorContainsPrivateIdentifierInExpression(decorator: Decorator) {
return !!(decorator.transformFlags & TransformFlags.ContainsPrivateIdentifierInExpression);
}

function parameterDecoratorsContainPrivateIdentifierInExpression(parameterDecorators: readonly Decorator[] | undefined) {
return some(parameterDecorators, decoratorContainsPrivateIdentifierInExpression);
}

function hasClassElementWithDecoratorContainingPrivateIdentifierInExpression(node: ClassDeclaration) {
for (const member of node.members) {
if (!canHaveDecorators(member)) continue;
const allDecorators = getAllDecoratorsOfClassElement(member, node);
if (some(allDecorators?.decorators, decoratorContainsPrivateIdentifierInExpression)) return true;
if (some(allDecorators?.parameters, parameterDecoratorsContainPrivateIdentifierInExpression)) return true;
}
return false;
}

function transformDecoratorsOfClassElements(node: ClassDeclaration, members: NodeArray<ClassElement>) {
let decorationStatements: Statement[] | undefined = [];
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ false);
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ true);
if (hasClassElementWithDecoratorContainingPrivateIdentifierInExpression(node)) {
members = setTextRange(factory.createNodeArray([
...members,
factory.createClassStaticBlockDeclaration(
factory.createBlock(decorationStatements, /*multiLine*/ true)
)
]), members);
decorationStatements = undefined;
}
return { decorationStatements, members };
}

/**
* Transforms a non-decorated class declaration.
*
* @param node A ClassDeclaration node.
* @param name The name of the class.
*/
function createClassDeclarationHeadWithoutDecorators(node: ClassDeclaration, name: Identifier | undefined) {
function transformClassDeclarationWithoutClassDecorators(node: ClassDeclaration, name: Identifier | undefined) {
// ${modifiers} class ${name} ${heritageClauses} {
// ${members}
// }

return factory.updateClassDeclaration(
const modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier);
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
let members = visitNodes(node.members, visitor, isClassElement);

let decorationStatements: Statement[] | undefined = [];
({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members));

const updated = factory.updateClassDeclaration(
node,
visitNodes(node.modifiers, modifierVisitor, isModifier),
modifiers,
name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
visitNodes(node.members, visitor, isClassElement)
heritageClauses,
members
);

return addRange([updated], decorationStatements);
}

/**
* Transforms a decorated class declaration and appends the resulting statements. If
* the class requires an alias to avoid issues with double-binding, the alias is returned.
*/
function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined) {
function transformClassDeclarationWithClassDecorators(node: ClassDeclaration, name: Identifier | undefined) {
// When we emit an ES6 class that has a class decorator, we must tailor the
// emit to certain specific cases.
//
Expand Down Expand Up @@ -213,8 +249,18 @@ namespace ts {
// ${members}
// }
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = visitNodes(node.members, visitor, isClassElement);
const classExpression = factory.createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
let members = visitNodes(node.members, visitor, isClassElement);

let decorationStatements: Statement[] | undefined = [];
({ members, decorationStatements } = transformDecoratorsOfClassElements(node, members));

const classExpression = factory.createClassExpression(
/*modifiers*/ undefined,
name,
/*typeParameters*/ undefined,
heritageClauses,
members);

setOriginalNode(classExpression, node);
setTextRange(classExpression, location);

Expand All @@ -234,7 +280,11 @@ namespace ts {
setOriginalNode(statement, node);
setTextRange(statement, location);
setCommentRange(statement, node);
return statement;

const statements: Statement[] = [statement];
addRange(statements, decorationStatements);
addConstructorDecorationStatement(statements, node);
return statements;
}

function visitClassExpression(node: ClassExpression) {
Expand Down
7 changes: 3 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7041,10 +7041,9 @@ namespace ts {
ContainsPossibleTopLevelAwait = 1 << 26,
ContainsLexicalSuper = 1 << 27,
ContainsUpdateExpressionForIdentifier = 1 << 28,
// Please leave this as 1 << 29.
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
// It is a good reminder of how much room we have left
HasComputedFlags = 1 << 29, // Transform flags have been computed.
ContainsPrivateIdentifierInExpression = 1 << 29,

HasComputedFlags = 1 << 31, // Transform flags have been computed.

// Assertions
// - Bitmasks that are used to assert facts about the syntax of a node and its subtree.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [decoratorOnClassMethod19.ts]
// https://github.com/microsoft/TypeScript/issues/48515
declare var decorator: any;

class C1 {
#x

@decorator((x: C1) => x.#x)
y() {}
}

class C2 {
#x

y(@decorator((x: C2) => x.#x) p) {}
}


//// [decoratorOnClassMethod19.js]
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
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 _C1_x, _C2_x;
class C1 {
constructor() {
_C1_x.set(this, void 0);
}
y() { }
}
_C1_x = new WeakMap();
(() => {
__decorate([
decorator((x) => __classPrivateFieldGet(x, _C1_x, "f")),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], C1.prototype, "y", null);
})();
class C2 {
constructor() {
_C2_x.set(this, void 0);
}
y(p) { }
}
_C2_x = new WeakMap();
(() => {
__decorate([
__param(0, decorator((x) => __classPrivateFieldGet(x, _C2_x, "f"))),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], C2.prototype, "y", null);
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== tests/cases/conformance/decorators/class/method/decoratorOnClassMethod19.ts ===
// https://github.com/microsoft/TypeScript/issues/48515
declare var decorator: any;
>decorator : Symbol(decorator, Decl(decoratorOnClassMethod19.ts, 1, 11))

class C1 {
>C1 : Symbol(C1, Decl(decoratorOnClassMethod19.ts, 1, 27))

#x
>#x : Symbol(C1.#x, Decl(decoratorOnClassMethod19.ts, 3, 10))

@decorator((x: C1) => x.#x)
>decorator : Symbol(decorator, Decl(decoratorOnClassMethod19.ts, 1, 11))
>x : Symbol(x, Decl(decoratorOnClassMethod19.ts, 6, 16))
>C1 : Symbol(C1, Decl(decoratorOnClassMethod19.ts, 1, 27))
>x.#x : Symbol(C1.#x, Decl(decoratorOnClassMethod19.ts, 3, 10))
>x : Symbol(x, Decl(decoratorOnClassMethod19.ts, 6, 16))

y() {}
>y : Symbol(C1.y, Decl(decoratorOnClassMethod19.ts, 4, 6))
}

class C2 {
>C2 : Symbol(C2, Decl(decoratorOnClassMethod19.ts, 8, 1))

#x
>#x : Symbol(C2.#x, Decl(decoratorOnClassMethod19.ts, 10, 10))

y(@decorator((x: C2) => x.#x) p) {}
>y : Symbol(C2.y, Decl(decoratorOnClassMethod19.ts, 11, 6))
>decorator : Symbol(decorator, Decl(decoratorOnClassMethod19.ts, 1, 11))
>x : Symbol(x, Decl(decoratorOnClassMethod19.ts, 13, 18))
>C2 : Symbol(C2, Decl(decoratorOnClassMethod19.ts, 8, 1))
>x.#x : Symbol(C2.#x, Decl(decoratorOnClassMethod19.ts, 10, 10))
>x : Symbol(x, Decl(decoratorOnClassMethod19.ts, 13, 18))
>p : Symbol(p, Decl(decoratorOnClassMethod19.ts, 13, 6))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
=== tests/cases/conformance/decorators/class/method/decoratorOnClassMethod19.ts ===
// https://github.com/microsoft/TypeScript/issues/48515
declare var decorator: any;
>decorator : any

class C1 {
>C1 : C1

#x
>#x : any

@decorator((x: C1) => x.#x)
>decorator((x: C1) => x.#x) : any
>decorator : any
>(x: C1) => x.#x : (x: C1) => any
>x : C1
>x.#x : any
>x : C1

y() {}
>y : () => void
}

class C2 {
>C2 : C2

#x
>#x : any

y(@decorator((x: C2) => x.#x) p) {}
>y : (p: any) => void
>decorator((x: C2) => x.#x) : any
>decorator : any
>(x: C2) => x.#x : (x: C2) => any
>x : C2
>x.#x : any
>x : C2
>p : any
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//// [decoratorOnClassMethod19.ts]
// https://github.com/microsoft/TypeScript/issues/48515
declare var decorator: any;

class C1 {
#x

@decorator((x: C1) => x.#x)
y() {}
}

class C2 {
#x

y(@decorator((x: C2) => x.#x) p) {}
}


//// [decoratorOnClassMethod19.js]
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
class C1 {
#x;
y() { }
static {
__decorate([
decorator((x) => x.#x),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], C1.prototype, "y", null);
}
}
class C2 {
#x;
y(p) { }
static {
__decorate([
__param(0, decorator((x) => x.#x)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], C2.prototype, "y", null);
}
}
Loading