Skip to content

Commit ecaf38f

Browse files
committed
Transform decorators that reference private names into a 'static {}' block
1 parent 8d0c72d commit ecaf38f

File tree

3 files changed

+87
-22
lines changed

3 files changed

+87
-22
lines changed

src/compiler/factory/nodeFactory.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2331,7 +2331,7 @@ namespace ts {
23312331
propagateChildFlags(node.expression) |
23322332
(isIdentifier(node.name) ?
23332333
propagateIdentifierNameFlags(node.name) :
2334-
propagateChildFlags(node.name));
2334+
propagateChildFlags(node.name) | TransformFlags.ContainsPrivateIdentifierInExpression);
23352335
if (isSuperKeyword(expression)) {
23362336
// super method calls require a lexical 'this'
23372337
// super method calls require 'super' hoisting in ES2017 and ES2018 async functions and async generators
@@ -2366,7 +2366,7 @@ namespace ts {
23662366
propagateChildFlags(node.questionDotToken) |
23672367
(isIdentifier(node.name) ?
23682368
propagateIdentifierNameFlags(node.name) :
2369-
propagateChildFlags(node.name));
2369+
propagateChildFlags(node.name) | TransformFlags.ContainsPrivateIdentifierInExpression);
23702370
return node;
23712371
}
23722372

@@ -2851,6 +2851,9 @@ namespace ts {
28512851
else if (isLogicalOrCoalescingAssignmentOperator(operatorKind)) {
28522852
node.transformFlags |= TransformFlags.ContainsES2021;
28532853
}
2854+
if (operatorKind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) {
2855+
node.transformFlags |= TransformFlags.ContainsPrivateIdentifierInExpression;
2856+
}
28542857
return node;
28552858
}
28562859

src/compiler/transformers/legacyDecorators.ts

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,46 @@ namespace ts {
6868
function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> {
6969
if (!(classOrConstructorParameterIsDecorated(node) || childIsDecorated(node))) return visitEachChild(node, visitor, context);
7070

71-
const classStatement = hasDecorators(node) ?
71+
const statements = hasDecorators(node) ?
7272
createClassDeclarationHeadWithDecorators(node, node.name) :
7373
createClassDeclarationHeadWithoutDecorators(node, node.name);
7474

75-
const statements: Statement[] = [classStatement];
76-
77-
// Write any decorators of the node.
78-
addClassElementDecorationStatements(statements, node, /*isStatic*/ false);
79-
addClassElementDecorationStatements(statements, node, /*isStatic*/ true);
80-
addConstructorDecorationStatement(statements, node);
81-
8275
if (statements.length > 1) {
8376
// Add a DeclarationMarker as a marker for the end of the declaration
8477
statements.push(factory.createEndOfDeclarationMarker(node));
85-
setEmitFlags(classStatement, getEmitFlags(classStatement) | EmitFlags.HasEndOfDeclarationMarker);
78+
setEmitFlags(statements[0], getEmitFlags(statements[0]) | EmitFlags.HasEndOfDeclarationMarker);
8679
}
8780

8881
return singleOrMany(statements);
8982
}
9083

84+
function containsDecoratedClassElementWithPrivateFieldAccess(node: ClassDeclaration) {
85+
for (const member of node.members) {
86+
if (!canHaveDecorators(member)) continue;
87+
const decorators = getAllDecoratorsOfClassElement(member, node);
88+
if (decorators?.decorators) {
89+
for (const decorator of decorators.decorators) {
90+
if (decorator.transformFlags & TransformFlags.ContainsPrivateIdentifierInExpression) {
91+
return true;
92+
}
93+
}
94+
}
95+
if (decorators?.parameters) {
96+
for (const parameterDecorators of decorators.parameters) {
97+
if (parameterDecorators) {
98+
for (const decorator of parameterDecorators) {
99+
if (decorator.transformFlags & TransformFlags.ContainsPrivateIdentifierInExpression) {
100+
return true;
101+
}
102+
}
103+
}
104+
}
105+
}
106+
}
107+
108+
return false;
109+
}
110+
91111
/**
92112
* Transforms a non-decorated class declaration.
93113
*
@@ -99,14 +119,33 @@ namespace ts {
99119
// ${members}
100120
// }
101121

102-
return factory.updateClassDeclaration(
122+
const modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier);
123+
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
124+
let members = visitNodes(node.members, visitor, isClassElement);
125+
126+
let decorationStatements: Statement[] | undefined = [];
127+
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ false);
128+
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ true);
129+
if (containsDecoratedClassElementWithPrivateFieldAccess(node)) {
130+
members = setTextRange(factory.createNodeArray([
131+
...members,
132+
factory.createClassStaticBlockDeclaration(
133+
factory.createBlock(decorationStatements, /*multiLine*/ true)
134+
)
135+
]), members);
136+
decorationStatements = undefined;
137+
}
138+
139+
const updated = factory.updateClassDeclaration(
103140
node,
104-
visitNodes(node.modifiers, modifierVisitor, isModifier),
141+
modifiers,
105142
name,
106143
/*typeParameters*/ undefined,
107-
visitNodes(node.heritageClauses, visitor, isHeritageClause),
108-
visitNodes(node.members, visitor, isClassElement)
144+
heritageClauses,
145+
members
109146
);
147+
148+
return addRange([updated], decorationStatements);
110149
}
111150

112151
/**
@@ -213,8 +252,28 @@ namespace ts {
213252
// ${members}
214253
// }
215254
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
216-
const members = visitNodes(node.members, visitor, isClassElement);
217-
const classExpression = factory.createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
255+
let members = visitNodes(node.members, visitor, isClassElement);
256+
257+
let decorationStatements: Statement[] | undefined = [];
258+
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ false);
259+
addClassElementDecorationStatements(decorationStatements, node, /*isStatic*/ true);
260+
if (containsDecoratedClassElementWithPrivateFieldAccess(node)) {
261+
members = setTextRange(factory.createNodeArray([
262+
...members,
263+
factory.createClassStaticBlockDeclaration(
264+
factory.createBlock(decorationStatements, /*multiLine*/ true)
265+
)
266+
]), members);
267+
decorationStatements = undefined;
268+
}
269+
270+
const classExpression = factory.createClassExpression(
271+
/*modifiers*/ undefined,
272+
name,
273+
/*typeParameters*/ undefined,
274+
heritageClauses,
275+
members);
276+
218277
setOriginalNode(classExpression, node);
219278
setTextRange(classExpression, location);
220279

@@ -234,7 +293,11 @@ namespace ts {
234293
setOriginalNode(statement, node);
235294
setTextRange(statement, location);
236295
setCommentRange(statement, node);
237-
return statement;
296+
297+
const statements: Statement[] = [statement];
298+
addRange(statements, decorationStatements);
299+
addConstructorDecorationStatement(statements, node);
300+
return statements;
238301
}
239302

240303
function visitClassExpression(node: ClassExpression) {

src/compiler/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7041,10 +7041,9 @@ namespace ts {
70417041
ContainsPossibleTopLevelAwait = 1 << 26,
70427042
ContainsLexicalSuper = 1 << 27,
70437043
ContainsUpdateExpressionForIdentifier = 1 << 28,
7044-
// Please leave this as 1 << 29.
7045-
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
7046-
// It is a good reminder of how much room we have left
7047-
HasComputedFlags = 1 << 29, // Transform flags have been computed.
7044+
ContainsPrivateIdentifierInExpression = 1 << 29,
7045+
7046+
HasComputedFlags = 1 << 31, // Transform flags have been computed.
70487047

70497048
// Assertions
70507049
// - Bitmasks that are used to assert facts about the syntax of a node and its subtree.

0 commit comments

Comments
 (0)