• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    const enum ClassPropertySubstitutionFlags {
4        /**
5         * Enables substitutions for class expressions with static fields
6         * which have initializers that reference the class name.
7         */
8        ClassAliases = 1 << 0,
9        /**
10         * Enables substitutions for class expressions with static fields
11         * which have initializers that reference the 'this' or 'super'.
12         */
13        ClassStaticThisOrSuperReference = 1 << 1,
14    }
15
16    export const enum PrivateIdentifierKind {
17        Field = "f",
18        Method = "m",
19        Accessor = "a"
20    }
21
22    interface PrivateIdentifierInfoBase {
23        /**
24         * brandCheckIdentifier can contain:
25         *  - For instance field: The WeakMap that will be the storage for the field.
26         *  - For instance methods or accessors: The WeakSet that will be used for brand checking.
27         *  - For static members: The constructor that will be used for brand checking.
28         */
29        brandCheckIdentifier: Identifier;
30        /**
31         * Stores if the identifier is static or not
32         */
33        isStatic: boolean;
34        /**
35         * Stores if the identifier declaration is valid or not. Reserved names (e.g. #constructor)
36         * or duplicate identifiers are considered invalid.
37         */
38        isValid: boolean;
39    }
40
41    interface PrivateIdentifierAccessorInfo extends PrivateIdentifierInfoBase {
42        kind: PrivateIdentifierKind.Accessor;
43        /**
44         * Identifier for a variable that will contain the private get accessor implementation, if any.
45         */
46        getterName?: Identifier;
47        /**
48         * Identifier for a variable that will contain the private set accessor implementation, if any.
49         */
50        setterName?: Identifier;
51    }
52
53    interface PrivateIdentifierMethodInfo extends PrivateIdentifierInfoBase {
54        kind: PrivateIdentifierKind.Method;
55        /**
56         * Identifier for a variable that will contain the private method implementation.
57         */
58        methodName: Identifier;
59    }
60
61    interface PrivateIdentifierInstanceFieldInfo extends PrivateIdentifierInfoBase {
62        kind: PrivateIdentifierKind.Field;
63        isStatic: false;
64        /**
65         * Defined for ease of access when in a union with PrivateIdentifierStaticFieldInfo.
66         */
67        variableName: undefined;
68    }
69
70    interface PrivateIdentifierStaticFieldInfo extends PrivateIdentifierInfoBase {
71        kind: PrivateIdentifierKind.Field;
72        isStatic: true;
73        /**
74         * Contains the variable that will serve as the storage for the field.
75         */
76        variableName: Identifier;
77    }
78
79    type PrivateIdentifierInfo =
80        | PrivateIdentifierMethodInfo
81        | PrivateIdentifierInstanceFieldInfo
82        | PrivateIdentifierStaticFieldInfo
83        | PrivateIdentifierAccessorInfo;
84
85    interface PrivateIdentifierEnvironment {
86        /**
87         * Used for prefixing generated variable names.
88         */
89        className?: Identifier;
90        /**
91         * Used for brand check on private methods.
92         */
93        weakSetName?: Identifier;
94        /**
95         * A mapping of private names to information needed for transformation.
96         */
97        identifiers?: UnderscoreEscapedMap<PrivateIdentifierInfo>;
98        /**
99         * A mapping of generated private names to information needed for transformation.
100         */
101        generatedIdentifiers?: ESMap<Node, PrivateIdentifierInfo>;
102    }
103
104    interface ClassLexicalEnvironment {
105        facts: ClassFacts;
106        /**
107         * Used for brand checks on static members, and `this` references in static initializers
108         */
109        classConstructor: Identifier | undefined;
110        /**
111         * Used for `super` references in static initializers.
112         */
113        superClassReference: Identifier | undefined;
114        privateIdentifierEnvironment: PrivateIdentifierEnvironment | undefined;
115    }
116
117    const enum ClassFacts {
118        None = 0,
119        ClassWasDecorated = 1 << 0,
120        NeedsClassConstructorReference = 1 << 1,
121        NeedsClassSuperReference = 1 << 2,
122        NeedsSubstitutionForThisInClassStaticField = 1 << 3,
123    }
124
125    /**
126     * Transforms ECMAScript Class Syntax.
127     * TypeScript parameter property syntax is transformed in the TypeScript transformer.
128     * For now, this transforms public field declarations using TypeScript class semantics,
129     * where declarations are elided and initializers are transformed as assignments in the constructor.
130     * When --useDefineForClassFields is on, this transforms to ECMAScript semantics, with Object.defineProperty.
131     */
132    export function transformClassFields(context: TransformationContext) {
133        const {
134            factory,
135            hoistVariableDeclaration,
136            endLexicalEnvironment,
137            startLexicalEnvironment,
138            resumeLexicalEnvironment,
139            addBlockScopedVariable
140        } = context;
141        const resolver = context.getEmitResolver();
142        const compilerOptions = context.getCompilerOptions();
143        const languageVersion = getEmitScriptTarget(compilerOptions);
144        const useDefineForClassFields = getUseDefineForClassFields(compilerOptions);
145
146        // Always transform field initializers using Set semantics when `useDefineForClassFields: false`.
147        const shouldTransformInitializersUsingSet = !useDefineForClassFields;
148
149        // Transform field initializers using Define semantics when `useDefineForClassFields: true` and target < ES2022.
150        const shouldTransformInitializersUsingDefine = useDefineForClassFields && languageVersion < ScriptTarget.ES2022;
151        const shouldTransformInitializers = shouldTransformInitializersUsingSet || shouldTransformInitializersUsingDefine;
152
153        // We need to transform private members and class static blocks when target < ES2022.
154        const shouldTransformPrivateElementsOrClassStaticBlocks = languageVersion < ScriptTarget.ES2022;
155
156        // We need to transform `accessor` fields when target < ESNext
157        const shouldTransformAutoAccessors = languageVersion < ScriptTarget.ESNext;
158
159        // We need to transform `this` in a static initializer into a reference to the class
160        // when target < ES2022 since the assignment will be moved outside of the class body.
161        const shouldTransformThisInStaticInitializers = languageVersion < ScriptTarget.ES2022;
162
163        // We don't need to transform `super` property access when target <= ES5 because
164        // the es2015 transformation handles those.
165        const shouldTransformSuperInStaticInitializers = shouldTransformThisInStaticInitializers && languageVersion >= ScriptTarget.ES2015;
166
167        const shouldTransformAnything =
168            shouldTransformInitializers ||
169            shouldTransformPrivateElementsOrClassStaticBlocks ||
170            shouldTransformAutoAccessors;
171
172        const previousOnSubstituteNode = context.onSubstituteNode;
173        context.onSubstituteNode = onSubstituteNode;
174
175        const previousOnEmitNode = context.onEmitNode;
176        context.onEmitNode = onEmitNode;
177
178        let enabledSubstitutions: ClassPropertySubstitutionFlags;
179
180        let classAliases: Identifier[];
181
182        /**
183         * Tracks what computed name expressions originating from elided names must be inlined
184         * at the next execution site, in document order
185         */
186        let pendingExpressions: Expression[] | undefined;
187
188        /**
189         * Tracks what computed name expression statements and static property initializers must be
190         * emitted at the next execution site, in document order (for decorated classes).
191         */
192        let pendingStatements: Statement[] | undefined;
193
194        const classLexicalEnvironmentStack: (ClassLexicalEnvironment | undefined)[] = [];
195        const classLexicalEnvironmentMap = new Map<number, ClassLexicalEnvironment>();
196
197        let currentClassLexicalEnvironment: ClassLexicalEnvironment | undefined;
198        let currentClassContainer: ClassLikeDeclaration | undefined;
199        let currentComputedPropertyNameClassLexicalEnvironment: ClassLexicalEnvironment | undefined;
200        let currentStaticPropertyDeclarationOrStaticBlock: PropertyDeclaration | ClassStaticBlockDeclaration | undefined;
201
202        return chainBundle(context, transformSourceFile);
203
204        function transformSourceFile(node: SourceFile) {
205            if (node.isDeclarationFile || !shouldTransformAnything) {
206                return node;
207            }
208
209            const visited = visitEachChild(node, visitor, context);
210            addEmitHelpers(visited, context.readEmitHelpers());
211            return visited;
212        }
213
214        function visitor(node: Node): VisitResult<Node> {
215            if (!(node.transformFlags & TransformFlags.ContainsClassFields) &&
216                !(node.transformFlags & TransformFlags.ContainsLexicalThisOrSuper)) {
217                return node;
218            }
219
220            switch (node.kind) {
221                case SyntaxKind.AccessorKeyword:
222                    return shouldTransformAutoAccessors ? undefined : node;
223                case SyntaxKind.ClassDeclaration:
224                    return visitClassDeclaration(node as ClassDeclaration);
225                case SyntaxKind.AnnotationDeclaration:
226                    return undefined;
227                case SyntaxKind.ClassExpression:
228                    return visitClassExpression(node as ClassExpression);
229                case SyntaxKind.ClassStaticBlockDeclaration:
230                    return visitClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
231                case SyntaxKind.PropertyDeclaration:
232                    return visitPropertyDeclaration(node as PropertyDeclaration);
233                case SyntaxKind.AnnotationPropertyDeclaration:
234                    return undefined;
235                case SyntaxKind.VariableStatement:
236                    return visitVariableStatement(node as VariableStatement);
237                case SyntaxKind.PrivateIdentifier:
238                    return visitPrivateIdentifier(node as PrivateIdentifier);
239                case SyntaxKind.PropertyAccessExpression:
240                    return visitPropertyAccessExpression(node as PropertyAccessExpression);
241                case SyntaxKind.ElementAccessExpression:
242                    return visitElementAccessExpression(node as ElementAccessExpression);
243                case SyntaxKind.PrefixUnaryExpression:
244                case SyntaxKind.PostfixUnaryExpression:
245                    return visitPreOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, /*valueIsDiscarded*/ false);
246                case SyntaxKind.BinaryExpression:
247                    return visitBinaryExpression(node as BinaryExpression, /*valueIsDiscarded*/ false);
248                case SyntaxKind.CallExpression:
249                    return visitCallExpression(node as CallExpression);
250                case SyntaxKind.ExpressionStatement:
251                    return visitExpressionStatement(node as ExpressionStatement);
252                case SyntaxKind.TaggedTemplateExpression:
253                    return visitTaggedTemplateExpression(node as TaggedTemplateExpression);
254                case SyntaxKind.ForStatement:
255                    return visitForStatement(node as ForStatement);
256                case SyntaxKind.FunctionDeclaration:
257                case SyntaxKind.FunctionExpression:
258                case SyntaxKind.Constructor:
259                case SyntaxKind.MethodDeclaration:
260                case SyntaxKind.GetAccessor:
261                case SyntaxKind.SetAccessor: {
262                    // If we are descending into a new scope, clear the current static property or block
263                    return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
264                        /*current*/ undefined,
265                        fallbackVisitor,
266                        node
267                    );
268                }
269                default:
270                    return fallbackVisitor(node);
271            }
272        }
273
274        function fallbackVisitor(node: Node) {
275            return visitEachChild(node, visitor, context);
276        }
277
278        /**
279         * Visits a node in an expression whose result is discarded.
280         */
281        function discardedValueVisitor(node: Node): VisitResult<Node> {
282            switch (node.kind) {
283                case SyntaxKind.PrefixUnaryExpression:
284                case SyntaxKind.PostfixUnaryExpression:
285                    return visitPreOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, /*valueIsDiscarded*/ true);
286                case SyntaxKind.BinaryExpression:
287                    return visitBinaryExpression(node as BinaryExpression, /*valueIsDiscarded*/ true);
288                default:
289                    return visitor(node);
290            }
291        }
292
293        /**
294         * Visits a node in a {@link HeritageClause}.
295         */
296        function heritageClauseVisitor(node: Node): VisitResult<Node> {
297            switch (node.kind) {
298                case SyntaxKind.HeritageClause:
299                    return visitEachChild(node, heritageClauseVisitor, context);
300                case SyntaxKind.ExpressionWithTypeArguments:
301                    return visitExpressionWithTypeArgumentsInHeritageClause(node as ExpressionWithTypeArguments);
302                default:
303                    return visitor(node);
304            }
305        }
306
307        /**
308         * Visits the assignment target of a destructuring assignment.
309         */
310        function assignmentTargetVisitor(node: Node): VisitResult<Node> {
311            switch (node.kind) {
312                case SyntaxKind.ObjectLiteralExpression:
313                case SyntaxKind.ArrayLiteralExpression:
314                    return visitAssignmentPattern(node as AssignmentPattern);
315                default:
316                    return visitor(node);
317            }
318        }
319
320        /**
321         * Visits a member of a class.
322         */
323        function classElementVisitor(node: Node): VisitResult<Node> {
324            switch (node.kind) {
325                case SyntaxKind.Constructor:
326                    return visitConstructorDeclaration(node as ConstructorDeclaration);
327                case SyntaxKind.GetAccessor:
328                case SyntaxKind.SetAccessor:
329                case SyntaxKind.MethodDeclaration:
330                    return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
331                        /*current*/ undefined,
332                        visitMethodOrAccessorDeclaration,
333                        node as MethodDeclaration | AccessorDeclaration);
334                case SyntaxKind.PropertyDeclaration:
335                    return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
336                        /*current*/ undefined,
337                        visitPropertyDeclaration,
338                        node as PropertyDeclaration);
339                case SyntaxKind.ComputedPropertyName:
340                    return visitComputedPropertyName(node as ComputedPropertyName);
341                case SyntaxKind.SemicolonClassElement:
342                    return node;
343                default:
344                    return visitor(node);
345            }
346        }
347
348        /**
349         * Visits the results of an auto-accessor field transformation in a second pass.
350         */
351        function accessorFieldResultVisitor(node: Node) {
352            switch (node.kind) {
353                case SyntaxKind.PropertyDeclaration:
354                    return transformFieldInitializer(node as PropertyDeclaration);
355                case SyntaxKind.GetAccessor:
356                case SyntaxKind.SetAccessor:
357                    return classElementVisitor(node);
358                default:
359                    Debug.assertMissingNode(node, "Expected node to either be a PropertyDeclaration, GetAccessorDeclaration, or SetAccessorDeclaration");
360                    break;
361            }
362        }
363
364        /**
365         * If we visit a private name, this means it is an undeclared private name.
366         * Replace it with an empty identifier to indicate a problem with the code,
367         * unless we are in a statement position - otherwise this will not trigger
368         * a SyntaxError.
369         */
370        function visitPrivateIdentifier(node: PrivateIdentifier) {
371            if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
372                return node;
373            }
374            if (isStatement(node.parent)) {
375                return node;
376            }
377            return setOriginalNode(factory.createIdentifier(""), node);
378        }
379
380        type PrivateIdentifierInExpression = BinaryExpression & { readonly left: PrivateIdentifier, readonly token: InKeyword };
381
382        function isPrivateIdentifierInExpression(node: BinaryExpression): node is PrivateIdentifierInExpression {
383            return isPrivateIdentifier(node.left)
384                && node.operatorToken.kind === SyntaxKind.InKeyword;
385        }
386
387        /**
388         * Visits `#id in expr`
389         */
390        function transformPrivateIdentifierInInExpression(node: PrivateIdentifierInExpression) {
391            const info = accessPrivateIdentifier(node.left);
392            if (info) {
393                const receiver = visitNode(node.right, visitor, isExpression);
394
395                return setOriginalNode(
396                    context.getEmitHelperFactory().createClassPrivateFieldInHelper(info.brandCheckIdentifier, receiver),
397                    node
398                );
399            }
400
401            // Private name has not been declared. Subsequent transformers will handle this error
402            return visitEachChild(node, visitor, context);
403        }
404
405        function visitVariableStatement(node: VariableStatement) {
406            const savedPendingStatements = pendingStatements;
407            pendingStatements = [];
408
409            const visitedNode = visitEachChild(node, visitor, context);
410            const statement = some(pendingStatements) ?
411                [visitedNode, ...pendingStatements] :
412                visitedNode;
413
414            pendingStatements = savedPendingStatements;
415            return statement;
416        }
417
418        function visitComputedPropertyName(node: ComputedPropertyName) {
419            let expression = visitNode(node.expression, visitor, isExpression);
420            if (some(pendingExpressions)) {
421                if (isParenthesizedExpression(expression)) {
422                    expression = factory.updateParenthesizedExpression(expression, factory.inlineExpressions([...pendingExpressions, expression.expression]));
423                }
424                else {
425                    expression = factory.inlineExpressions([...pendingExpressions, expression]);
426                }
427                pendingExpressions = undefined;
428            }
429            return factory.updateComputedPropertyName(node, expression);
430        }
431
432        function visitConstructorDeclaration(node: ConstructorDeclaration) {
433            if (currentClassContainer) {
434                return transformConstructor(node, currentClassContainer);
435            }
436            return fallbackVisitor(node);
437        }
438
439        function visitMethodOrAccessorDeclaration(node: MethodDeclaration | AccessorDeclaration) {
440            Debug.assert(!hasDecorators(node));
441
442            if (!shouldTransformPrivateElementsOrClassStaticBlocks || !isPrivateIdentifier(node.name)) {
443                return visitEachChild(node, classElementVisitor, context);
444            }
445
446            // leave invalid code untransformed
447            const info = accessPrivateIdentifier(node.name);
448            Debug.assert(info, "Undeclared private name for property declaration.");
449            if (!info.isValid) {
450                return node;
451            }
452
453            const functionName = getHoistedFunctionName(node);
454            if (functionName) {
455                getPendingExpressions().push(
456                    factory.createAssignment(
457                        functionName,
458                        factory.createFunctionExpression(
459                            filter(node.modifiers, (m): m is Modifier => isModifier(m) && !isStaticModifier(m) && !isAccessorModifier(m)),
460                            node.asteriskToken,
461                            functionName,
462                            /* typeParameters */ undefined,
463                            visitParameterList(node.parameters, visitor, context),
464                            /* type */ undefined,
465                            visitFunctionBody(node.body!, visitor, context)
466                        )
467                    )
468                );
469            }
470
471            // remove method declaration from class
472            return undefined;
473        }
474
475        function setCurrentStaticPropertyDeclarationOrStaticBlockAnd<T, U>(
476            current: ClassStaticBlockDeclaration | PropertyDeclaration | undefined,
477            visitor: (arg: T) => U,
478            arg: T,
479        ) {
480            const savedCurrentStaticPropertyDeclarationOrStaticBlock = currentStaticPropertyDeclarationOrStaticBlock;
481            currentStaticPropertyDeclarationOrStaticBlock = current;
482            const result = visitor(arg);
483            currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock;
484            return result;
485        }
486
487        function getHoistedFunctionName(node: MethodDeclaration | AccessorDeclaration) {
488            Debug.assert(isPrivateIdentifier(node.name));
489            const info = accessPrivateIdentifier(node.name);
490            Debug.assert(info, "Undeclared private name for property declaration.");
491
492            if (info.kind === PrivateIdentifierKind.Method) {
493                return info.methodName;
494            }
495
496            if (info.kind === PrivateIdentifierKind.Accessor) {
497                if (isGetAccessor(node)) {
498                    return info.getterName;
499                }
500                if (isSetAccessor(node)) {
501                    return info.setterName;
502                }
503            }
504        }
505
506        function transformAutoAccessor(node: AutoAccessorPropertyDeclaration): VisitResult<Node> {
507            // transforms:
508            //      accessor x = 1;
509            // into:
510            //      #x = 1;
511            //      get x() { return this.#x; }
512            //      set x(value) { this.#x = value; }
513
514            Debug.assertEachNode(node.modifiers, isModifier);
515
516            const commentRange = getCommentRange(node);
517            const sourceMapRange = getSourceMapRange(node);
518
519            // Since we're creating two declarations where there was previously one, cache
520            // the expression for any computed property names.
521            const name = node.name;
522            let getterName = name;
523            let setterName = name;
524            if (isComputedPropertyName(name) && !isSimpleInlineableExpression(name.expression)) {
525                const temp = factory.createTempVariable(hoistVariableDeclaration);
526                setSourceMapRange(temp, name.expression);
527                const expression = visitNode(name.expression, visitor, isExpression);
528                const assignment = factory.createAssignment(temp, expression);
529                setSourceMapRange(assignment, name.expression);
530                getterName = factory.updateComputedPropertyName(name, factory.inlineExpressions([assignment, temp]));
531                setterName = factory.updateComputedPropertyName(name, temp);
532            }
533
534            const backingField = createAccessorPropertyBackingField(factory, node, node.modifiers, node.initializer);
535            setOriginalNode(backingField, node);
536            setEmitFlags(backingField, EmitFlags.NoComments);
537            setSourceMapRange(backingField, sourceMapRange);
538
539            const getter = createAccessorPropertyGetRedirector(factory, node, node.modifiers, getterName);
540            setOriginalNode(getter, node);
541            setCommentRange(getter, commentRange);
542            setSourceMapRange(getter, sourceMapRange);
543
544            const setter = createAccessorPropertySetRedirector(factory, node, node.modifiers, setterName);
545            setOriginalNode(setter, node);
546            setEmitFlags(setter, EmitFlags.NoComments);
547            setSourceMapRange(setter, sourceMapRange);
548
549            return visitArray([backingField, getter, setter], accessorFieldResultVisitor, isClassElement);
550        }
551
552        function transformPrivateFieldInitializer(node: PrivateIdentifierPropertyDeclaration) {
553            if (shouldTransformPrivateElementsOrClassStaticBlocks) {
554                // If we are transforming private elements into WeakMap/WeakSet, we should elide the node.
555                const info = accessPrivateIdentifier(node.name);
556                Debug.assert(info, "Undeclared private name for property declaration.");
557
558                // Leave invalid code untransformed; otherwise, elide the node as it is transformed elsewhere.
559                return info.isValid ? undefined : node;
560            }
561
562            if (shouldTransformInitializersUsingSet && !isStatic(node)) {
563                // If we are transforming initializers using Set semantics we will elide the initializer as it will
564                // be moved to the constructor to preserve evaluation order next to public instance fields. We don't
565                // need to do this transformation for private static fields since public static fields can be
566                // transformed into `static {}` blocks.
567                return factory.updatePropertyDeclaration(
568                    node,
569                    visitNodes(node.modifiers, visitor, isModifierLike),
570                    node.name,
571                    /*questionOrExclamationToken*/ undefined,
572                    /*type*/ undefined,
573                    /*initializer*/ undefined
574                );
575            }
576
577            return visitEachChild(node, visitor, context);
578        }
579
580        function transformPublicFieldInitializer(node: PropertyDeclaration) {
581            if (shouldTransformInitializers) {
582                // Create a temporary variable to store a computed property name (if necessary).
583                // If it's not inlineable, then we emit an expression after the class which assigns
584                // the property name to the temporary variable.
585
586                const expr = getPropertyNameExpressionIfNeeded(node.name, /*shouldHoist*/ !!node.initializer || useDefineForClassFields);
587                if (expr) {
588                    getPendingExpressions().push(expr);
589                }
590
591                if (isStatic(node) && !shouldTransformPrivateElementsOrClassStaticBlocks) {
592                    const initializerStatement = transformPropertyOrClassStaticBlock(node, factory.createThis());
593                    if (initializerStatement) {
594                        const staticBlock = factory.createClassStaticBlockDeclaration(
595                            factory.createBlock([initializerStatement])
596                        );
597
598                        setOriginalNode(staticBlock, node);
599                        setCommentRange(staticBlock, node);
600
601                        // Set the comment range for the statement to an empty synthetic range
602                        // and drop synthetic comments from the statement to avoid printing them twice.
603                        setCommentRange(initializerStatement, { pos: -1, end: -1 });
604                        setSyntheticLeadingComments(initializerStatement, undefined);
605                        setSyntheticTrailingComments(initializerStatement, undefined);
606                        return staticBlock;
607                    }
608                }
609
610                return undefined;
611            }
612
613            return visitEachChild(node, classElementVisitor, context);
614        }
615
616        function transformFieldInitializer(node: PropertyDeclaration) {
617            Debug.assert(!hasDecorators(node), "Decorators should already have been transformed and elided.");
618            return isPrivateIdentifierClassElementDeclaration(node) ?
619                transformPrivateFieldInitializer(node) :
620                transformPublicFieldInitializer(node);
621        }
622
623        function visitPropertyDeclaration(node: PropertyDeclaration) {
624            // If this is an auto-accessor, we defer to `transformAutoAccessor`. That function
625            // will in turn call `transformFieldInitializer` as needed.
626            if (shouldTransformAutoAccessors && isAutoAccessorPropertyDeclaration(node)) {
627                return transformAutoAccessor(node);
628            }
629
630            return transformFieldInitializer(node);
631        }
632
633        function createPrivateIdentifierAccess(info: PrivateIdentifierInfo, receiver: Expression): Expression {
634            return createPrivateIdentifierAccessHelper(info, visitNode(receiver, visitor, isExpression));
635        }
636
637        function createPrivateIdentifierAccessHelper(info: PrivateIdentifierInfo, receiver: Expression): Expression {
638            setCommentRange(receiver, moveRangePos(receiver, -1));
639
640            switch(info.kind) {
641                case PrivateIdentifierKind.Accessor:
642                    return context.getEmitHelperFactory().createClassPrivateFieldGetHelper(
643                        receiver,
644                        info.brandCheckIdentifier,
645                        info.kind,
646                        info.getterName
647                    );
648                case PrivateIdentifierKind.Method:
649                    return context.getEmitHelperFactory().createClassPrivateFieldGetHelper(
650                        receiver,
651                        info.brandCheckIdentifier,
652                        info.kind,
653                        info.methodName
654                    );
655                case PrivateIdentifierKind.Field:
656                    return context.getEmitHelperFactory().createClassPrivateFieldGetHelper(
657                        receiver,
658                        info.brandCheckIdentifier,
659                        info.kind,
660                        info.variableName
661                    );
662                default:
663                    Debug.assertNever(info, "Unknown private element type");
664            }
665        }
666
667        function visitPropertyAccessExpression(node: PropertyAccessExpression) {
668            if (shouldTransformPrivateElementsOrClassStaticBlocks && isPrivateIdentifier(node.name)) {
669                const privateIdentifierInfo = accessPrivateIdentifier(node.name);
670                if (privateIdentifierInfo) {
671                    return setTextRange(
672                        setOriginalNode(
673                            createPrivateIdentifierAccess(privateIdentifierInfo, node.expression),
674                            node
675                        ),
676                        node
677                    );
678                }
679            }
680            if (shouldTransformSuperInStaticInitializers &&
681                isSuperProperty(node) &&
682                isIdentifier(node.name) &&
683                currentStaticPropertyDeclarationOrStaticBlock &&
684                currentClassLexicalEnvironment) {
685                const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
686                if (facts & ClassFacts.ClassWasDecorated) {
687                    return visitInvalidSuperProperty(node);
688                }
689                if (classConstructor && superClassReference) {
690                    // converts `super.x` into `Reflect.get(_baseTemp, "x", _classTemp)`
691                    const superProperty = factory.createReflectGetCall(
692                        superClassReference,
693                        factory.createStringLiteralFromNode(node.name),
694                        classConstructor
695                    );
696                    setOriginalNode(superProperty, node.expression);
697                    setTextRange(superProperty, node.expression);
698                    return superProperty;
699                }
700            }
701            return visitEachChild(node, visitor, context);
702        }
703
704        function visitElementAccessExpression(node: ElementAccessExpression) {
705            if (shouldTransformSuperInStaticInitializers &&
706                isSuperProperty(node) &&
707                currentStaticPropertyDeclarationOrStaticBlock &&
708                currentClassLexicalEnvironment) {
709                const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
710                if (facts & ClassFacts.ClassWasDecorated) {
711                    return visitInvalidSuperProperty(node);
712                }
713
714                if (classConstructor && superClassReference) {
715                    // converts `super[x]` into `Reflect.get(_baseTemp, x, _classTemp)`
716                    const superProperty = factory.createReflectGetCall(
717                        superClassReference,
718                        visitNode(node.argumentExpression, visitor, isExpression),
719                        classConstructor
720                    );
721                    setOriginalNode(superProperty, node.expression);
722                    setTextRange(superProperty, node.expression);
723                    return superProperty;
724                }
725            }
726            return visitEachChild(node, visitor, context);
727        }
728
729        function visitPreOrPostfixUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded: boolean) {
730            if (node.operator === SyntaxKind.PlusPlusToken ||
731                node.operator === SyntaxKind.MinusMinusToken) {
732                const operand = skipParentheses(node.operand);
733                if (shouldTransformPrivateElementsOrClassStaticBlocks &&
734                    isPrivateIdentifierPropertyAccessExpression(operand)) {
735                    let info: PrivateIdentifierInfo | undefined;
736                    if (info = accessPrivateIdentifier(operand.name)) {
737                        const receiver = visitNode(operand.expression, visitor, isExpression);
738                        const { readExpression, initializeExpression } = createCopiableReceiverExpr(receiver);
739
740                        let expression: Expression = createPrivateIdentifierAccess(info, readExpression);
741                        const temp = isPrefixUnaryExpression(node) || valueIsDiscarded ? undefined : factory.createTempVariable(hoistVariableDeclaration);
742                        expression = expandPreOrPostfixIncrementOrDecrementExpression(factory, node, expression, hoistVariableDeclaration, temp);
743                        expression = createPrivateIdentifierAssignment(
744                            info,
745                            initializeExpression || readExpression,
746                            expression,
747                            SyntaxKind.EqualsToken
748                        );
749                        setOriginalNode(expression, node);
750                        setTextRange(expression, node);
751                        if (temp) {
752                            expression = factory.createComma(expression, temp);
753                            setTextRange(expression, node);
754                        }
755                        return expression;
756                    }
757                }
758                else if (shouldTransformSuperInStaticInitializers &&
759                    isSuperProperty(operand) &&
760                    currentStaticPropertyDeclarationOrStaticBlock &&
761                    currentClassLexicalEnvironment) {
762                    // converts `++super.a` into `(Reflect.set(_baseTemp, "a", (_a = Reflect.get(_baseTemp, "a", _classTemp), _b = ++_a), _classTemp), _b)`
763                    // converts `++super[f()]` into `(Reflect.set(_baseTemp, _a = f(), (_b = Reflect.get(_baseTemp, _a, _classTemp), _c = ++_b), _classTemp), _c)`
764                    // converts `--super.a` into `(Reflect.set(_baseTemp, "a", (_a = Reflect.get(_baseTemp, "a", _classTemp), _b = --_a), _classTemp), _b)`
765                    // converts `--super[f()]` into `(Reflect.set(_baseTemp, _a = f(), (_b = Reflect.get(_baseTemp, _a, _classTemp), _c = --_b), _classTemp), _c)`
766                    // converts `super.a++` into `(Reflect.set(_baseTemp, "a", (_a = Reflect.get(_baseTemp, "a", _classTemp), _b = _a++), _classTemp), _b)`
767                    // converts `super[f()]++` into `(Reflect.set(_baseTemp, _a = f(), (_b = Reflect.get(_baseTemp, _a, _classTemp), _c = _b++), _classTemp), _c)`
768                    // converts `super.a--` into `(Reflect.set(_baseTemp, "a", (_a = Reflect.get(_baseTemp, "a", _classTemp), _b = _a--), _classTemp), _b)`
769                    // converts `super[f()]--` into `(Reflect.set(_baseTemp, _a = f(), (_b = Reflect.get(_baseTemp, _a, _classTemp), _c = _b--), _classTemp), _c)`
770                    const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
771                    if (facts & ClassFacts.ClassWasDecorated) {
772                        const expression = visitInvalidSuperProperty(operand);
773                        return isPrefixUnaryExpression(node) ?
774                            factory.updatePrefixUnaryExpression(node, expression) :
775                            factory.updatePostfixUnaryExpression(node, expression);
776                    }
777                    if (classConstructor && superClassReference) {
778                        let setterName: Expression | undefined;
779                        let getterName: Expression | undefined;
780                        if (isPropertyAccessExpression(operand)) {
781                            if (isIdentifier(operand.name)) {
782                                getterName = setterName = factory.createStringLiteralFromNode(operand.name);
783                            }
784                        }
785                        else {
786                            if (isSimpleInlineableExpression(operand.argumentExpression)) {
787                                getterName = setterName = operand.argumentExpression;
788                            }
789                            else {
790                                getterName = factory.createTempVariable(hoistVariableDeclaration);
791                                setterName = factory.createAssignment(getterName, visitNode(operand.argumentExpression, visitor, isExpression));
792                            }
793                        }
794                        if (setterName && getterName) {
795                            let expression: Expression = factory.createReflectGetCall(superClassReference, getterName, classConstructor);
796                            setTextRange(expression, operand);
797
798                            const temp = valueIsDiscarded ? undefined : factory.createTempVariable(hoistVariableDeclaration);
799                            expression = expandPreOrPostfixIncrementOrDecrementExpression(factory, node, expression, hoistVariableDeclaration, temp);
800                            expression = factory.createReflectSetCall(superClassReference, setterName, expression, classConstructor);
801                            setOriginalNode(expression, node);
802                            setTextRange(expression, node);
803                            if (temp) {
804                                expression = factory.createComma(expression, temp);
805                                setTextRange(expression, node);
806                            }
807                            return expression;
808                        }
809                    }
810                }
811            }
812            return visitEachChild(node, visitor, context);
813        }
814
815        function visitForStatement(node: ForStatement) {
816            return factory.updateForStatement(
817                node,
818                visitNode(node.initializer, discardedValueVisitor, isForInitializer),
819                visitNode(node.condition, visitor, isExpression),
820                visitNode(node.incrementor, discardedValueVisitor, isExpression),
821                visitIterationBody(node.statement, visitor, context)
822            );
823        }
824
825        function visitExpressionStatement(node: ExpressionStatement) {
826            return factory.updateExpressionStatement(
827                node,
828                visitNode(node.expression, discardedValueVisitor, isExpression)
829            );
830        }
831
832        function createCopiableReceiverExpr(receiver: Expression): { readExpression: Expression; initializeExpression: Expression | undefined } {
833            const clone = nodeIsSynthesized(receiver) ? receiver : factory.cloneNode(receiver);
834            if (isSimpleInlineableExpression(receiver)) {
835                return { readExpression: clone, initializeExpression: undefined };
836            }
837            const readExpression = factory.createTempVariable(hoistVariableDeclaration);
838            const initializeExpression = factory.createAssignment(readExpression, clone);
839            return { readExpression, initializeExpression };
840        }
841
842        function visitCallExpression(node: CallExpression) {
843            if (shouldTransformPrivateElementsOrClassStaticBlocks &&
844                isPrivateIdentifierPropertyAccessExpression(node.expression)) {
845                // obj.#x()
846
847                // Transform call expressions of private names to properly bind the `this` parameter.
848                const { thisArg, target } = factory.createCallBinding(node.expression, hoistVariableDeclaration, languageVersion);
849                if (isCallChain(node)) {
850                    return factory.updateCallChain(
851                        node,
852                        factory.createPropertyAccessChain(visitNode(target, visitor), node.questionDotToken, "call"),
853                        /*questionDotToken*/ undefined,
854                        /*typeArguments*/ undefined,
855                        [visitNode(thisArg, visitor, isExpression), ...visitNodes(node.arguments, visitor, isExpression)]
856                    );
857                }
858                return factory.updateCallExpression(
859                    node,
860                    factory.createPropertyAccessExpression(visitNode(target, visitor), "call"),
861                    /*typeArguments*/ undefined,
862                    [visitNode(thisArg, visitor, isExpression), ...visitNodes(node.arguments, visitor, isExpression)]
863                );
864            }
865
866            if (shouldTransformSuperInStaticInitializers &&
867                isSuperProperty(node.expression) &&
868                currentStaticPropertyDeclarationOrStaticBlock &&
869                currentClassLexicalEnvironment?.classConstructor) {
870                // super.x()
871                // super[x]()
872
873                // converts `super.f(...)` into `Reflect.get(_baseTemp, "f", _classTemp).call(_classTemp, ...)`
874                const invocation = factory.createFunctionCallCall(
875                    visitNode(node.expression, visitor, isExpression),
876                    currentClassLexicalEnvironment.classConstructor,
877                    visitNodes(node.arguments, visitor, isExpression)
878                );
879                setOriginalNode(invocation, node);
880                setTextRange(invocation, node);
881                return invocation;
882            }
883
884            return visitEachChild(node, visitor, context);
885        }
886
887        function visitTaggedTemplateExpression(node: TaggedTemplateExpression) {
888            if (shouldTransformPrivateElementsOrClassStaticBlocks &&
889                isPrivateIdentifierPropertyAccessExpression(node.tag)) {
890                // Bind the `this` correctly for tagged template literals when the tag is a private identifier property access.
891                const { thisArg, target } = factory.createCallBinding(node.tag, hoistVariableDeclaration, languageVersion);
892                return factory.updateTaggedTemplateExpression(
893                    node,
894                    factory.createCallExpression(
895                        factory.createPropertyAccessExpression(visitNode(target, visitor), "bind"),
896                        /*typeArguments*/ undefined,
897                        [visitNode(thisArg, visitor, isExpression)]
898                    ),
899                    /*typeArguments*/ undefined,
900                    visitNode(node.template, visitor, isTemplateLiteral)
901                );
902            }
903            if (shouldTransformSuperInStaticInitializers &&
904                isSuperProperty(node.tag) &&
905                currentStaticPropertyDeclarationOrStaticBlock &&
906                currentClassLexicalEnvironment?.classConstructor) {
907
908                // converts `` super.f`x` `` into `` Reflect.get(_baseTemp, "f", _classTemp).bind(_classTemp)`x` ``
909                const invocation = factory.createFunctionBindCall(
910                    visitNode(node.tag, visitor, isExpression),
911                    currentClassLexicalEnvironment.classConstructor,
912                    []
913                );
914                setOriginalNode(invocation, node);
915                setTextRange(invocation, node);
916                return factory.updateTaggedTemplateExpression(
917                    node,
918                    invocation,
919                    /*typeArguments*/ undefined,
920                    visitNode(node.template, visitor, isTemplateLiteral)
921                );
922            }
923            return visitEachChild(node, visitor, context);
924        }
925
926        function transformClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
927            if (shouldTransformPrivateElementsOrClassStaticBlocks) {
928                if (currentClassLexicalEnvironment) {
929                    classLexicalEnvironmentMap.set(getOriginalNodeId(node), currentClassLexicalEnvironment);
930                }
931
932                startLexicalEnvironment();
933                let statements = setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
934                    node,
935                    statements => visitNodes(statements, visitor, isStatement),
936                    node.body.statements
937                );
938                statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
939
940                const iife = factory.createImmediatelyInvokedArrowFunction(statements);
941                setOriginalNode(iife, node);
942                setTextRange(iife, node);
943                addEmitFlags(iife, EmitFlags.AdviseOnEmitNode);
944                return iife;
945            }
946        }
947
948        function visitBinaryExpression(node: BinaryExpression, valueIsDiscarded: boolean) {
949            if (isDestructuringAssignment(node)) {
950                // ({ x: obj.#x } = ...)
951                // ({ x: super.x } = ...)
952                // ({ x: super[x] } = ...)
953                const savedPendingExpressions = pendingExpressions;
954                pendingExpressions = undefined;
955                node = factory.updateBinaryExpression(
956                    node,
957                    visitNode(node.left, assignmentTargetVisitor),
958                    node.operatorToken,
959                    visitNode(node.right, visitor)
960                );
961                const expr = some(pendingExpressions) ?
962                    factory.inlineExpressions(compact([...pendingExpressions, node])) :
963                    node;
964                pendingExpressions = savedPendingExpressions;
965                return expr;
966            }
967            if (isAssignmentExpression(node)) {
968                if (shouldTransformPrivateElementsOrClassStaticBlocks &&
969                    isPrivateIdentifierPropertyAccessExpression(node.left)) {
970                    // obj.#x = ...
971                    const info = accessPrivateIdentifier(node.left.name);
972                    if (info) {
973                        return setTextRange(
974                            setOriginalNode(
975                                createPrivateIdentifierAssignment(info, node.left.expression, node.right, node.operatorToken.kind),
976                                node
977                            ),
978                            node
979                        );
980                    }
981                }
982                else if (shouldTransformSuperInStaticInitializers &&
983                    isSuperProperty(node.left) &&
984                    currentStaticPropertyDeclarationOrStaticBlock &&
985                    currentClassLexicalEnvironment) {
986                    // super.x = ...
987                    // super[x] = ...
988                    // super.x += ...
989                    // super.x -= ...
990                    const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
991                    if (facts & ClassFacts.ClassWasDecorated) {
992                        return factory.updateBinaryExpression(
993                            node,
994                            visitInvalidSuperProperty(node.left),
995                            node.operatorToken,
996                            visitNode(node.right, visitor, isExpression));
997                    }
998                    if (classConstructor && superClassReference) {
999                        let setterName =
1000                            isElementAccessExpression(node.left) ? visitNode(node.left.argumentExpression, visitor, isExpression) :
1001                            isIdentifier(node.left.name) ? factory.createStringLiteralFromNode(node.left.name) :
1002                            undefined;
1003                        if (setterName) {
1004                            // converts `super.x = 1` into `(Reflect.set(_baseTemp, "x", _a = 1, _classTemp), _a)`
1005                            // converts `super[f()] = 1` into `(Reflect.set(_baseTemp, f(), _a = 1, _classTemp), _a)`
1006                            // converts `super.x += 1` into `(Reflect.set(_baseTemp, "x", _a = Reflect.get(_baseTemp, "x", _classtemp) + 1, _classTemp), _a)`
1007                            // converts `super[f()] += 1` into `(Reflect.set(_baseTemp, _a = f(), _b = Reflect.get(_baseTemp, _a, _classtemp) + 1, _classTemp), _b)`
1008
1009                            let expression = visitNode(node.right, visitor, isExpression);
1010                            if (isCompoundAssignment(node.operatorToken.kind)) {
1011                                let getterName = setterName;
1012                                if (!isSimpleInlineableExpression(setterName)) {
1013                                    getterName = factory.createTempVariable(hoistVariableDeclaration);
1014                                    setterName = factory.createAssignment(getterName, setterName);
1015                                }
1016                                const superPropertyGet = factory.createReflectGetCall(
1017                                    superClassReference,
1018                                    getterName,
1019                                    classConstructor
1020                                );
1021                                setOriginalNode(superPropertyGet, node.left);
1022                                setTextRange(superPropertyGet, node.left);
1023
1024                                expression = factory.createBinaryExpression(
1025                                    superPropertyGet,
1026                                    getNonAssignmentOperatorForCompoundAssignment(node.operatorToken.kind),
1027                                    expression
1028                                );
1029                                setTextRange(expression, node);
1030                            }
1031
1032                            const temp = valueIsDiscarded ? undefined : factory.createTempVariable(hoistVariableDeclaration);
1033                            if (temp) {
1034                                expression = factory.createAssignment(temp, expression);
1035                                setTextRange(temp, node);
1036                            }
1037
1038                            expression = factory.createReflectSetCall(
1039                                superClassReference,
1040                                setterName,
1041                                expression,
1042                                classConstructor
1043                            );
1044                            setOriginalNode(expression, node);
1045                            setTextRange(expression, node);
1046
1047                            if (temp) {
1048                                expression = factory.createComma(expression, temp);
1049                                setTextRange(expression, node);
1050                            }
1051
1052                            return expression;
1053                        }
1054                    }
1055                }
1056            }
1057            if (shouldTransformPrivateElementsOrClassStaticBlocks &&
1058                isPrivateIdentifierInExpression(node)) {
1059                // #x in obj
1060                return transformPrivateIdentifierInInExpression(node);
1061            }
1062            return visitEachChild(node, visitor, context);
1063        }
1064
1065        function createPrivateIdentifierAssignment(info: PrivateIdentifierInfo, receiver: Expression, right: Expression, operator: AssignmentOperator): Expression {
1066            receiver = visitNode(receiver, visitor, isExpression);
1067            right = visitNode(right, visitor, isExpression);
1068
1069            if (isCompoundAssignment(operator)) {
1070                const { readExpression, initializeExpression } = createCopiableReceiverExpr(receiver);
1071                receiver = initializeExpression || readExpression;
1072                right = factory.createBinaryExpression(
1073                    createPrivateIdentifierAccessHelper(info, readExpression),
1074                    getNonAssignmentOperatorForCompoundAssignment(operator),
1075                    right
1076                );
1077            }
1078
1079            setCommentRange(receiver, moveRangePos(receiver, -1));
1080
1081            switch(info.kind) {
1082                case PrivateIdentifierKind.Accessor:
1083                    return context.getEmitHelperFactory().createClassPrivateFieldSetHelper(
1084                        receiver,
1085                        info.brandCheckIdentifier,
1086                        right,
1087                        info.kind,
1088                        info.setterName
1089                    );
1090                case PrivateIdentifierKind.Method:
1091                    return context.getEmitHelperFactory().createClassPrivateFieldSetHelper(
1092                        receiver,
1093                        info.brandCheckIdentifier,
1094                        right,
1095                        info.kind,
1096                        /* f */ undefined
1097                    );
1098                case PrivateIdentifierKind.Field:
1099                    return context.getEmitHelperFactory().createClassPrivateFieldSetHelper(
1100                        receiver,
1101                        info.brandCheckIdentifier,
1102                        right,
1103                        info.kind,
1104                        info.variableName
1105                    );
1106                default:
1107                    Debug.assertNever(info, "Unknown private element type");
1108            }
1109        }
1110
1111        function getPrivateInstanceMethodsAndAccessors(node: ClassLikeDeclaration) {
1112            return filter(node.members, isNonStaticMethodOrAccessorWithPrivateName);
1113        }
1114
1115        function getClassFacts(node: ClassLikeDeclaration) {
1116            let facts = ClassFacts.None;
1117            const original = getOriginalNode(node);
1118            if (isClassDeclaration(original) && classOrConstructorParameterIsDecorated(original)) {
1119                facts |= ClassFacts.ClassWasDecorated;
1120            }
1121            for (const member of node.members) {
1122                if (!isStatic(member)) continue;
1123                if (member.name && (isPrivateIdentifier(member.name) || isAutoAccessorPropertyDeclaration(member)) && shouldTransformPrivateElementsOrClassStaticBlocks) {
1124                    facts |= ClassFacts.NeedsClassConstructorReference;
1125                }
1126                if (isPropertyDeclaration(member) || isClassStaticBlockDeclaration(member)) {
1127                    if (shouldTransformThisInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalThis) {
1128                        facts |= ClassFacts.NeedsSubstitutionForThisInClassStaticField;
1129                        if (!(facts & ClassFacts.ClassWasDecorated)) {
1130                            facts |= ClassFacts.NeedsClassConstructorReference;
1131                        }
1132                    }
1133                    if (shouldTransformSuperInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalSuper) {
1134                        if (!(facts & ClassFacts.ClassWasDecorated)) {
1135                            facts |= ClassFacts.NeedsClassConstructorReference | ClassFacts.NeedsClassSuperReference;
1136                        }
1137                    }
1138                }
1139            }
1140            return facts;
1141        }
1142
1143        function visitExpressionWithTypeArgumentsInHeritageClause(node: ExpressionWithTypeArguments) {
1144            const facts = currentClassLexicalEnvironment?.facts || ClassFacts.None;
1145            if (facts & ClassFacts.NeedsClassSuperReference) {
1146                const temp = factory.createTempVariable(hoistVariableDeclaration, /*reserveInNestedScopes*/ true);
1147                getClassLexicalEnvironment().superClassReference = temp;
1148                return factory.updateExpressionWithTypeArguments(
1149                    node,
1150                    factory.createAssignment(
1151                        temp,
1152                        visitNode(node.expression, visitor, isExpression)
1153                    ),
1154                    /*typeArguments*/ undefined
1155                );
1156            }
1157            return visitEachChild(node, visitor, context);
1158        }
1159
1160        function visitInNewClassLexicalEnvironment<T extends ClassLikeDeclaration, U>(node: T, visitor: (node: T, facts: ClassFacts) => U) {
1161            const savedCurrentClassContainer = currentClassContainer;
1162            const savedPendingExpressions = pendingExpressions;
1163            currentClassContainer = node;
1164            pendingExpressions = undefined;
1165            startClassLexicalEnvironment();
1166
1167            if (shouldTransformPrivateElementsOrClassStaticBlocks) {
1168                const name = getNameOfDeclaration(node);
1169                if (name && isIdentifier(name)) {
1170                    getPrivateIdentifierEnvironment().className = name;
1171                }
1172
1173                const privateInstanceMethodsAndAccessors = getPrivateInstanceMethodsAndAccessors(node);
1174                if (some(privateInstanceMethodsAndAccessors)) {
1175                    getPrivateIdentifierEnvironment().weakSetName = createHoistedVariableForClass(
1176                        "instances",
1177                        privateInstanceMethodsAndAccessors[0].name
1178                    );
1179                }
1180            }
1181
1182            const facts = getClassFacts(node);
1183            if (facts) {
1184                getClassLexicalEnvironment().facts = facts;
1185            }
1186
1187            if (facts & ClassFacts.NeedsSubstitutionForThisInClassStaticField) {
1188                enableSubstitutionForClassStaticThisOrSuperReference();
1189            }
1190
1191            const result = visitor(node, facts);
1192            endClassLexicalEnvironment();
1193            currentClassContainer = savedCurrentClassContainer;
1194            pendingExpressions = savedPendingExpressions;
1195            return result;
1196
1197        }
1198
1199        function visitClassDeclaration(node: ClassDeclaration) {
1200            return visitInNewClassLexicalEnvironment(node, visitClassDeclarationInNewClassLexicalEnvironment);
1201        }
1202
1203        function visitClassDeclarationInNewClassLexicalEnvironment(node: ClassDeclaration, facts: ClassFacts) {
1204            // If a class has private static fields, or a static field has a `this` or `super` reference,
1205            // then we need to allocate a temp variable to hold on to that reference.
1206            let pendingClassReferenceAssignment: BinaryExpression | undefined;
1207            if (facts & ClassFacts.NeedsClassConstructorReference) {
1208                const temp = factory.createTempVariable(hoistVariableDeclaration, /*reservedInNestedScopes*/ true);
1209                getClassLexicalEnvironment().classConstructor = factory.cloneNode(temp);
1210                pendingClassReferenceAssignment = factory.createAssignment(temp, factory.getInternalName(node));
1211            }
1212
1213            const modifiers = visitNodes(node.modifiers, visitor, isModifierLike);
1214            const heritageClauses = visitNodes(node.heritageClauses, heritageClauseVisitor, isHeritageClause);
1215            const { members, prologue } = transformClassMembers(node);
1216            const classDecl = factory.updateClassDeclaration(
1217                node,
1218                modifiers,
1219                node.name,
1220                /*typeParameters*/ undefined,
1221                heritageClauses,
1222                members
1223            );
1224
1225            const statements: Statement[] = [];
1226            if (prologue) {
1227                statements.push(factory.createExpressionStatement(prologue));
1228            }
1229
1230            statements.push(classDecl);
1231
1232            if (pendingClassReferenceAssignment) {
1233                getPendingExpressions().unshift(pendingClassReferenceAssignment);
1234            }
1235
1236            // Write any pending expressions from elided or moved computed property names
1237            if (some(pendingExpressions)) {
1238                statements.push(factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions)));
1239            }
1240
1241            if (shouldTransformInitializersUsingSet || shouldTransformPrivateElementsOrClassStaticBlocks) {
1242                // Emit static property assignment. Because classDeclaration is lexically evaluated,
1243                // it is safe to emit static property assignment after classDeclaration
1244                // From ES6 specification:
1245                //      HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
1246                //                                  a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
1247
1248                const staticProperties = getStaticPropertiesAndClassStaticBlock(node);
1249                if (some(staticProperties)) {
1250                    addPropertyOrClassStaticBlockStatements(statements, staticProperties, factory.getInternalName(node));
1251                }
1252            }
1253
1254            return statements;
1255        }
1256
1257        function visitClassExpression(node: ClassExpression): Expression {
1258            return visitInNewClassLexicalEnvironment(node, visitClassExpressionInNewClassLexicalEnvironment);
1259        }
1260
1261        function visitClassExpressionInNewClassLexicalEnvironment(node: ClassExpression, facts: ClassFacts): Expression {
1262            // If this class expression is a transformation of a decorated class declaration,
1263            // then we want to output the pendingExpressions as statements, not as inlined
1264            // expressions with the class statement.
1265            //
1266            // In this case, we use pendingStatements to produce the same output as the
1267            // class declaration transformation. The VariableStatement visitor will insert
1268            // these statements after the class expression variable statement.
1269            const isDecoratedClassDeclaration = !!(facts & ClassFacts.ClassWasDecorated);
1270
1271            const staticPropertiesOrClassStaticBlocks = getStaticPropertiesAndClassStaticBlock(node);
1272
1273            const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference;
1274            let temp: Identifier | undefined;
1275            function createClassTempVar() {
1276                const classCheckFlags = resolver.getNodeCheckFlags(node);
1277                const isClassWithConstructorReference = classCheckFlags & NodeCheckFlags.ClassWithConstructorReference;
1278                const requiresBlockScopedVar = classCheckFlags & NodeCheckFlags.BlockScopedBindingInLoop;
1279                return factory.createTempVariable(requiresBlockScopedVar ? addBlockScopedVariable : hoistVariableDeclaration, !!isClassWithConstructorReference);
1280            }
1281
1282            if (facts & ClassFacts.NeedsClassConstructorReference) {
1283                temp = createClassTempVar();
1284                getClassLexicalEnvironment().classConstructor = factory.cloneNode(temp);
1285            }
1286
1287            const modifiers = visitNodes(node.modifiers, visitor, isModifierLike);
1288            const heritageClauses = visitNodes(node.heritageClauses, heritageClauseVisitor, isHeritageClause);
1289            const { members, prologue } = transformClassMembers(node);
1290            const classExpression = factory.updateClassExpression(
1291                node,
1292                modifiers,
1293                node.name,
1294                /*typeParameters*/ undefined,
1295                heritageClauses,
1296                members
1297            );
1298
1299            const expressions: Expression[] = [];
1300            if (prologue) {
1301                expressions.push(prologue);
1302            }
1303
1304            // Static initializers are transformed to `static {}` blocks when `useDefineForClassFields: false`
1305            // and not also transforming static blocks.
1306            const hasTransformableStatics =
1307                shouldTransformPrivateElementsOrClassStaticBlocks &&
1308                some(staticPropertiesOrClassStaticBlocks, node =>
1309                    isClassStaticBlockDeclaration(node) ||
1310                    isPrivateIdentifierClassElementDeclaration(node) ||
1311                    shouldTransformInitializers && isInitializedProperty(node));
1312
1313            if (hasTransformableStatics || some(pendingExpressions)) {
1314                if (isDecoratedClassDeclaration) {
1315                    Debug.assertIsDefined(pendingStatements, "Decorated classes transformed by TypeScript are expected to be within a variable declaration.");
1316
1317                    // Write any pending expressions from elided or moved computed property names
1318                    if (pendingStatements && pendingExpressions && some(pendingExpressions)) {
1319                        pendingStatements.push(factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions)));
1320                    }
1321
1322                    if (pendingStatements && some(staticPropertiesOrClassStaticBlocks)) {
1323                        addPropertyOrClassStaticBlockStatements(pendingStatements, staticPropertiesOrClassStaticBlocks, factory.getInternalName(node));
1324                    }
1325
1326                    if (temp) {
1327                        expressions.push(
1328                            startOnNewLine(factory.createAssignment(temp, classExpression)),
1329                            startOnNewLine(temp));
1330                    }
1331                    else {
1332                        expressions.push(classExpression);
1333                        if (prologue) {
1334                            startOnNewLine(classExpression);
1335                        }
1336                    }
1337                }
1338                else {
1339                    temp ||= createClassTempVar();
1340                    if (isClassWithConstructorReference) {
1341                        // record an alias as the class name is not in scope for statics.
1342                        enableSubstitutionForClassAliases();
1343                        const alias = factory.cloneNode(temp) as GeneratedIdentifier;
1344                        alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes;
1345                        classAliases[getOriginalNodeId(node)] = alias;
1346                    }
1347
1348                    // To preserve the behavior of the old emitter, we explicitly indent
1349                    // the body of a class with static initializers.
1350                    setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression));
1351                    expressions.push(startOnNewLine(factory.createAssignment(temp, classExpression)));
1352                    // Add any pending expressions leftover from elided or relocated computed property names
1353                    addRange(expressions, map(pendingExpressions, startOnNewLine));
1354                    addRange(expressions, generateInitializedPropertyExpressionsOrClassStaticBlock(staticPropertiesOrClassStaticBlocks, temp));
1355                    expressions.push(startOnNewLine(temp));
1356                }
1357            }
1358            else {
1359                expressions.push(classExpression);
1360                if (prologue) {
1361                    startOnNewLine(classExpression);
1362                }
1363            }
1364
1365            return factory.inlineExpressions(expressions);
1366        }
1367
1368        function visitClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) {
1369            if (!shouldTransformPrivateElementsOrClassStaticBlocks) {
1370                return visitEachChild(node, visitor, context);
1371            }
1372            // ClassStaticBlockDeclaration for classes are transformed in `visitClassDeclaration` or `visitClassExpression`.
1373            return undefined;
1374        }
1375
1376        function transformClassMembers(node: ClassDeclaration | ClassExpression) {
1377            // Declare private names
1378            if (shouldTransformPrivateElementsOrClassStaticBlocks) {
1379                for (const member of node.members) {
1380                    if (isPrivateIdentifierClassElementDeclaration(member)) {
1381                        addPrivateIdentifierToEnvironment(member, member.name, addPrivateIdentifierClassElementToEnvironment);
1382                    }
1383                }
1384                if (some(getPrivateInstanceMethodsAndAccessors(node))) {
1385                    createBrandCheckWeakSetForPrivateMethods();
1386                }
1387                if (shouldTransformAutoAccessors) {
1388                    for (const member of node.members) {
1389                        if (isAutoAccessorPropertyDeclaration(member)) {
1390                            const storageName = factory.getGeneratedPrivateNameForNode(member.name, /*prefix*/ undefined, "_accessor_storage");
1391                            addPrivateIdentifierToEnvironment(member, storageName, addPrivateIdentifierPropertyDeclarationToEnvironment);
1392                        }
1393                    }
1394                }
1395            }
1396
1397            let members = visitNodes(node.members, classElementVisitor, isClassElement);
1398
1399            // Create a synthetic constructor if necessary
1400            let syntheticConstructor: ConstructorDeclaration | undefined;
1401            if (!some(members, isConstructorDeclaration)) {
1402                syntheticConstructor = transformConstructor(/*constructor*/ undefined, node);
1403            }
1404
1405            let prologue: Expression | undefined;
1406
1407            // If there are pending expressions create a class static block in which to evaluate them, but only if
1408            // class static blocks are not also being transformed. This block will be injected at the top of the class
1409            // to ensure that expressions from computed property names are evaluated before any other static
1410            // initializers.
1411            let syntheticStaticBlock: ClassStaticBlockDeclaration | undefined;
1412            if (!shouldTransformPrivateElementsOrClassStaticBlocks && some(pendingExpressions)) {
1413                let statement = factory.createExpressionStatement(factory.inlineExpressions(pendingExpressions));
1414                if (statement.transformFlags & TransformFlags.ContainsLexicalThisOrSuper) {
1415                    // If there are `this` or `super` references from computed property names, shift the expression
1416                    // into an arrow function to be evaluated in the outer scope so that `this` and `super` are
1417                    // properly captured.
1418                    const temp = factory.createTempVariable(hoistVariableDeclaration);
1419                    const arrow = factory.createArrowFunction(
1420                        /*modifiers*/ undefined,
1421                        /*typeParameters*/ undefined,
1422                        /*parameters*/ [],
1423                        /*type*/ undefined,
1424                        /*equalsGreaterThanToken*/ undefined,
1425                        factory.createBlock([statement]));
1426                    prologue = factory.createAssignment(temp, arrow);
1427                    statement = factory.createExpressionStatement(factory.createCallExpression(temp, /*typeArguments*/ undefined, []));
1428                }
1429
1430                const block = factory.createBlock([statement]);
1431                syntheticStaticBlock = factory.createClassStaticBlockDeclaration(block);
1432                pendingExpressions = undefined;
1433            }
1434
1435            // If we created a synthetic constructor or class static block, add them to the visited members
1436            // and return a new array.
1437            if (syntheticConstructor || syntheticStaticBlock) {
1438                let membersArray: ClassElement[] | undefined;
1439                membersArray = append(membersArray, syntheticConstructor);
1440                membersArray = append(membersArray, syntheticStaticBlock);
1441                membersArray = addRange(membersArray, members);
1442                members = setTextRange(factory.createNodeArray(membersArray), /*location*/ node.members);
1443            }
1444
1445            return { members, prologue };
1446        }
1447
1448        function createBrandCheckWeakSetForPrivateMethods() {
1449            const { weakSetName } = getPrivateIdentifierEnvironment();
1450            Debug.assert(weakSetName, "weakSetName should be set in private identifier environment");
1451
1452            getPendingExpressions().push(
1453                factory.createAssignment(
1454                    weakSetName,
1455                    factory.createNewExpression(
1456                        factory.createIdentifier("WeakSet"),
1457                        /*typeArguments*/ undefined,
1458                        []
1459                    )
1460                )
1461            );
1462        }
1463
1464        function isClassElementThatRequiresConstructorStatement(member: ClassElement) {
1465            if (isStatic(member) || hasAbstractModifier(getOriginalNode(member))) {
1466                return false;
1467            }
1468
1469            return shouldTransformInitializersUsingDefine && isPropertyDeclaration(member) ||
1470                shouldTransformInitializersUsingSet && isInitializedProperty(member) ||
1471                shouldTransformPrivateElementsOrClassStaticBlocks && isPrivateIdentifierClassElementDeclaration(member) ||
1472                shouldTransformPrivateElementsOrClassStaticBlocks && shouldTransformAutoAccessors && isAutoAccessorPropertyDeclaration(member);
1473        }
1474
1475        function transformConstructor(constructor: ConstructorDeclaration | undefined, container: ClassDeclaration | ClassExpression | StructDeclaration) {
1476            constructor = visitNode(constructor, visitor, isConstructorDeclaration);
1477            if (!some(container.members, isClassElementThatRequiresConstructorStatement)) {
1478                return constructor;
1479            }
1480
1481            const extendsClauseElement = getEffectiveBaseTypeNode(container);
1482            const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword);
1483            const parameters = visitParameterList(constructor ? constructor.parameters : undefined, visitor, context);
1484            const body = transformConstructorBody(container, constructor, isDerivedClass);
1485            if (!body) {
1486                return constructor;
1487            }
1488
1489            if (constructor) {
1490                Debug.assert(parameters);
1491                return factory.updateConstructorDeclaration(constructor, /*modifiers*/ undefined, parameters, body);
1492            }
1493
1494            return startOnNewLine(
1495                setOriginalNode(
1496                    setTextRange(
1497                        factory.createConstructorDeclaration(
1498                            /*modifiers*/ undefined,
1499                            parameters ?? [],
1500                            body
1501                        ),
1502                        constructor || container
1503                    ),
1504                    constructor
1505                )
1506            );
1507        }
1508
1509        function transformConstructorBody(node: ClassDeclaration | ClassExpression | StructDeclaration, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) {
1510            let properties = getProperties(node, /*requireInitializer*/ false, /*isStatic*/ false);
1511            if (!useDefineForClassFields) {
1512                properties = filter(properties, property => !!property.initializer || isPrivateIdentifier(property.name) || hasAccessorModifier(property));
1513            }
1514
1515            const privateMethodsAndAccessors = getPrivateInstanceMethodsAndAccessors(node);
1516            const needsConstructorBody = some(properties) || some(privateMethodsAndAccessors);
1517
1518            // Only generate synthetic constructor when there are property initializers to move.
1519            if (!constructor && !needsConstructorBody) {
1520                return visitFunctionBody(/*node*/ undefined, visitor, context);
1521            }
1522
1523            resumeLexicalEnvironment();
1524
1525            const needsSyntheticConstructor = !constructor && isDerivedClass;
1526            let indexOfFirstStatementAfterSuperAndPrologue = 0;
1527            let prologueStatementCount = 0;
1528            let superStatementIndex = -1;
1529            let statements: Statement[] = [];
1530
1531            if (constructor?.body?.statements) {
1532                prologueStatementCount = factory.copyPrologue(constructor.body.statements, statements, /*ensureUseStrict*/ false, visitor);
1533                superStatementIndex = findSuperStatementIndex(constructor.body.statements, prologueStatementCount);
1534
1535                // If there was a super call, visit existing statements up to and including it
1536                if (superStatementIndex >= 0) {
1537                    indexOfFirstStatementAfterSuperAndPrologue = superStatementIndex + 1;
1538                    statements = [
1539                        ...statements.slice(0, prologueStatementCount),
1540                        ...visitNodes(constructor.body.statements, visitor, isStatement, prologueStatementCount, indexOfFirstStatementAfterSuperAndPrologue - prologueStatementCount),
1541                        ...statements.slice(prologueStatementCount),
1542                    ];
1543                }
1544                else if (prologueStatementCount >= 0) {
1545                    indexOfFirstStatementAfterSuperAndPrologue = prologueStatementCount;
1546                }
1547            }
1548
1549            if (needsSyntheticConstructor) {
1550                // Add a synthetic `super` call:
1551                //
1552                //  super(...arguments);
1553                //
1554                statements.push(
1555                    factory.createExpressionStatement(
1556                        factory.createCallExpression(
1557                            factory.createSuper(),
1558                            /*typeArguments*/ undefined,
1559                            [factory.createSpreadElement(factory.createIdentifier("arguments"))]
1560                        )
1561                    )
1562                );
1563            }
1564
1565            // Add the property initializers. Transforms this:
1566            //
1567            //  public x = 1;
1568            //
1569            // Into this:
1570            //
1571            //  constructor() {
1572            //      this.x = 1;
1573            //  }
1574            //
1575            // If we do useDefineForClassFields, they'll be converted elsewhere.
1576            // We instead *remove* them from the transformed output at this stage.
1577            let parameterPropertyDeclarationCount = 0;
1578            if (constructor?.body) {
1579                if (useDefineForClassFields) {
1580                    statements = statements.filter(statement => !isParameterPropertyDeclaration(getOriginalNode(statement), constructor));
1581                }
1582                else {
1583                    for (const statement of constructor.body.statements) {
1584                        if (isParameterPropertyDeclaration(getOriginalNode(statement), constructor)) {
1585                            parameterPropertyDeclarationCount++;
1586                        }
1587                    }
1588                    if (parameterPropertyDeclarationCount > 0) {
1589                        const parameterProperties = visitNodes(constructor.body.statements, visitor, isStatement, indexOfFirstStatementAfterSuperAndPrologue, parameterPropertyDeclarationCount);
1590
1591                        // If there was a super() call found, add parameter properties immediately after it
1592                        if (superStatementIndex >= 0) {
1593                            addRange(statements, parameterProperties);
1594                        }
1595                        else {
1596                            // Add add parameter properties to the top of the constructor after the prologue
1597                            let superAndPrologueStatementCount = prologueStatementCount;
1598                            // If a synthetic super() call was added, need to account for that
1599                            if (needsSyntheticConstructor) superAndPrologueStatementCount++;
1600                            statements = [
1601                                ...statements.slice(0, superAndPrologueStatementCount),
1602                                ...parameterProperties,
1603                                ...statements.slice(superAndPrologueStatementCount),
1604                            ];
1605                        }
1606
1607                        indexOfFirstStatementAfterSuperAndPrologue += parameterPropertyDeclarationCount;
1608                    }
1609                }
1610            }
1611
1612            const receiver = factory.createThis();
1613            // private methods can be called in property initializers, they should execute first.
1614            addMethodStatements(statements, privateMethodsAndAccessors, receiver);
1615            addPropertyOrClassStaticBlockStatements(statements, properties, receiver);
1616
1617            // Add existing statements after the initial prologues and super call
1618            if (constructor) {
1619                addRange(statements, visitNodes(constructor.body!.statements, visitBodyStatement, isStatement, indexOfFirstStatementAfterSuperAndPrologue));
1620            }
1621
1622            statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
1623
1624            if (statements.length === 0 && !constructor) {
1625                return undefined;
1626            }
1627
1628            const multiLine = constructor?.body && constructor.body.statements.length >= statements.length ?
1629                constructor.body.multiLine ?? statements.length > 0 :
1630                statements.length > 0;
1631
1632            return setTextRange(
1633                factory.createBlock(
1634                    setTextRange(
1635                        factory.createNodeArray(statements),
1636                        /*location*/ constructor ? constructor.body!.statements : node.members
1637                    ),
1638                    multiLine
1639                ),
1640                /*location*/ constructor ? constructor.body : undefined
1641            );
1642
1643            function visitBodyStatement(statement: Node) {
1644                if (useDefineForClassFields && isParameterPropertyDeclaration(getOriginalNode(statement), constructor!)) {
1645                    return undefined;
1646                }
1647
1648                return visitor(statement);
1649            }
1650        }
1651
1652        /**
1653         * Generates assignment statements for property initializers.
1654         *
1655         * @param properties An array of property declarations to transform.
1656         * @param receiver The receiver on which each property should be assigned.
1657         */
1658        function addPropertyOrClassStaticBlockStatements(statements: Statement[], properties: readonly (PropertyDeclaration | ClassStaticBlockDeclaration)[], receiver: LeftHandSideExpression) {
1659            for (const property of properties) {
1660                if (isStatic(property) && !shouldTransformPrivateElementsOrClassStaticBlocks && !useDefineForClassFields) {
1661                    continue;
1662                }
1663
1664                const statement = transformPropertyOrClassStaticBlock(property, receiver);
1665                if (!statement) {
1666                    continue;
1667                }
1668
1669                statements.push(statement);
1670            }
1671        }
1672
1673        function transformPropertyOrClassStaticBlock(property: PropertyDeclaration | ClassStaticBlockDeclaration, receiver: LeftHandSideExpression) {
1674            const expression = isClassStaticBlockDeclaration(property) ?
1675                transformClassStaticBlockDeclaration(property) :
1676                transformProperty(property, receiver);
1677            if (!expression) {
1678                return undefined;
1679            }
1680
1681            const statement = factory.createExpressionStatement(expression);
1682            setOriginalNode(statement, property);
1683            addEmitFlags(statement, getEmitFlags(property) & EmitFlags.NoComments);
1684            setSourceMapRange(statement, moveRangePastModifiers(property));
1685            setCommentRange(statement, property);
1686
1687            // `setOriginalNode` *copies* the `emitNode` from `property`, so now both
1688            // `statement` and `expression` have a copy of the synthesized comments.
1689            // Drop the comments from expression to avoid printing them twice.
1690            setSyntheticLeadingComments(expression, undefined);
1691            setSyntheticTrailingComments(expression, undefined);
1692
1693            return statement;
1694        }
1695
1696        /**
1697         * Generates assignment expressions for property initializers.
1698         *
1699         * @param propertiesOrClassStaticBlocks An array of property declarations to transform.
1700         * @param receiver The receiver on which each property should be assigned.
1701         */
1702        function generateInitializedPropertyExpressionsOrClassStaticBlock(propertiesOrClassStaticBlocks: readonly (PropertyDeclaration | ClassStaticBlockDeclaration)[], receiver: LeftHandSideExpression) {
1703            const expressions: Expression[] = [];
1704            for (const property of propertiesOrClassStaticBlocks) {
1705                const expression = isClassStaticBlockDeclaration(property) ? transformClassStaticBlockDeclaration(property) : transformProperty(property, receiver);
1706                if (!expression) {
1707                    continue;
1708                }
1709                startOnNewLine(expression);
1710                setOriginalNode(expression, property);
1711                addEmitFlags(expression, getEmitFlags(property) & EmitFlags.NoComments);
1712                setSourceMapRange(expression, moveRangePastModifiers(property));
1713                setCommentRange(expression, property);
1714                expressions.push(expression);
1715            }
1716
1717            return expressions;
1718        }
1719
1720        /**
1721         * Transforms a property initializer into an assignment statement.
1722         *
1723         * @param property The property declaration.
1724         * @param receiver The object receiving the property assignment.
1725         */
1726        function transformProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
1727            const savedCurrentStaticPropertyDeclarationOrStaticBlock = currentStaticPropertyDeclarationOrStaticBlock;
1728            const transformed = transformPropertyWorker(property, receiver);
1729            if (transformed && hasStaticModifier(property) && currentClassLexicalEnvironment?.facts) {
1730                // capture the lexical environment for the member
1731                setOriginalNode(transformed, property);
1732                addEmitFlags(transformed, EmitFlags.AdviseOnEmitNode);
1733                classLexicalEnvironmentMap.set(getOriginalNodeId(transformed), currentClassLexicalEnvironment);
1734            }
1735            currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock;
1736            return transformed;
1737        }
1738
1739        function transformPropertyWorker(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
1740            // We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name)
1741            const emitAssignment = !useDefineForClassFields;
1742
1743            const propertyName =
1744                hasAccessorModifier(property) ?
1745                    factory.getGeneratedPrivateNameForNode(property.name) :
1746                isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression) ?
1747                    factory.updateComputedPropertyName(property.name, factory.getGeneratedNameForNode(property.name)) :
1748                    property.name;
1749
1750            if (hasStaticModifier(property)) {
1751                currentStaticPropertyDeclarationOrStaticBlock = property;
1752            }
1753
1754            if (shouldTransformPrivateElementsOrClassStaticBlocks && isPrivateIdentifier(propertyName)) {
1755                const privateIdentifierInfo = accessPrivateIdentifier(propertyName);
1756                if (privateIdentifierInfo) {
1757                    if (privateIdentifierInfo.kind === PrivateIdentifierKind.Field) {
1758                        if (!privateIdentifierInfo.isStatic) {
1759                            return createPrivateInstanceFieldInitializer(
1760                                receiver,
1761                                visitNode(property.initializer, visitor, isExpression),
1762                                privateIdentifierInfo.brandCheckIdentifier
1763                            );
1764                        }
1765                        else {
1766                            return createPrivateStaticFieldInitializer(
1767                                privateIdentifierInfo.variableName,
1768                                visitNode(property.initializer, visitor, isExpression)
1769                            );
1770                        }
1771                    }
1772                    else {
1773                        return undefined;
1774                    }
1775                }
1776                else {
1777                    Debug.fail("Undeclared private name for property declaration.");
1778                }
1779            }
1780            if ((isPrivateIdentifier(propertyName) || hasStaticModifier(property)) && !property.initializer) {
1781                return undefined;
1782            }
1783
1784            const propertyOriginalNode = getOriginalNode(property);
1785            if (hasSyntacticModifier(propertyOriginalNode, ModifierFlags.Abstract)) {
1786                return undefined;
1787            }
1788
1789            const initializer = property.initializer || emitAssignment ? visitNode(property.initializer, visitor, isExpression) ?? factory.createVoidZero()
1790                : isParameterPropertyDeclaration(propertyOriginalNode, propertyOriginalNode.parent) && isIdentifier(propertyName) ? propertyName
1791                : factory.createVoidZero();
1792
1793            if (emitAssignment || isPrivateIdentifier(propertyName)) {
1794                const memberAccess = createMemberAccessForPropertyName(factory, receiver, propertyName, /*location*/ propertyName);
1795                return factory.createAssignment(memberAccess, initializer);
1796            }
1797            else {
1798                const name = isComputedPropertyName(propertyName) ? propertyName.expression
1799                    : isIdentifier(propertyName) ? factory.createStringLiteral(unescapeLeadingUnderscores(propertyName.escapedText))
1800                    : propertyName;
1801                const descriptor = factory.createPropertyDescriptor({ value: initializer, configurable: true, writable: true, enumerable: true });
1802                return factory.createObjectDefinePropertyCall(receiver, name, descriptor);
1803            }
1804        }
1805
1806        function enableSubstitutionForClassAliases() {
1807            if ((enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) === 0) {
1808                enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassAliases;
1809
1810                // We need to enable substitutions for identifiers. This allows us to
1811                // substitute class names inside of a class declaration.
1812                context.enableSubstitution(SyntaxKind.Identifier);
1813
1814                // Keep track of class aliases.
1815                classAliases = [];
1816            }
1817        }
1818
1819        function enableSubstitutionForClassStaticThisOrSuperReference() {
1820            if ((enabledSubstitutions & ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference) === 0) {
1821                enabledSubstitutions |= ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference;
1822
1823                // substitute `this` in a static field initializer
1824                context.enableSubstitution(SyntaxKind.ThisKeyword);
1825
1826                // these push a new lexical environment that is not the class lexical environment
1827                context.enableEmitNotification(SyntaxKind.FunctionDeclaration);
1828                context.enableEmitNotification(SyntaxKind.FunctionExpression);
1829                context.enableEmitNotification(SyntaxKind.Constructor);
1830
1831                // these push a new lexical environment that is not the class lexical environment, except
1832                // when they have a computed property name
1833                context.enableEmitNotification(SyntaxKind.GetAccessor);
1834                context.enableEmitNotification(SyntaxKind.SetAccessor);
1835                context.enableEmitNotification(SyntaxKind.MethodDeclaration);
1836                context.enableEmitNotification(SyntaxKind.PropertyDeclaration);
1837
1838                // class lexical environments are restored when entering a computed property name
1839                context.enableEmitNotification(SyntaxKind.ComputedPropertyName);
1840            }
1841        }
1842
1843        /**
1844         * Generates brand-check initializer for private methods.
1845         *
1846         * @param statements Statement list that should be used to append new statements.
1847         * @param methods An array of method declarations.
1848         * @param receiver The receiver on which each method should be assigned.
1849         */
1850        function addMethodStatements(statements: Statement[], methods: readonly (MethodDeclaration | AccessorDeclaration | AutoAccessorPropertyDeclaration)[], receiver: LeftHandSideExpression) {
1851            if (!shouldTransformPrivateElementsOrClassStaticBlocks || !some(methods)) {
1852                return;
1853            }
1854
1855            const { weakSetName } = getPrivateIdentifierEnvironment();
1856            Debug.assert(weakSetName, "weakSetName should be set in private identifier environment");
1857            statements.push(
1858                factory.createExpressionStatement(
1859                    createPrivateInstanceMethodInitializer(receiver, weakSetName)
1860                )
1861            );
1862        }
1863
1864        function visitInvalidSuperProperty(node: SuperProperty) {
1865            return isPropertyAccessExpression(node) ?
1866                factory.updatePropertyAccessExpression(
1867                    node,
1868                    factory.createVoidZero(),
1869                    node.name) :
1870                factory.updateElementAccessExpression(
1871                    node,
1872                    factory.createVoidZero(),
1873                    visitNode(node.argumentExpression, visitor, isExpression));
1874        }
1875
1876        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
1877            const original = getOriginalNode(node);
1878            if (original.id) {
1879                const classLexicalEnvironment = classLexicalEnvironmentMap.get(original.id);
1880                if (classLexicalEnvironment) {
1881                    const savedClassLexicalEnvironment = currentClassLexicalEnvironment;
1882                    const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment;
1883                    currentClassLexicalEnvironment = classLexicalEnvironment;
1884                    currentComputedPropertyNameClassLexicalEnvironment = classLexicalEnvironment;
1885                    previousOnEmitNode(hint, node, emitCallback);
1886                    currentClassLexicalEnvironment = savedClassLexicalEnvironment;
1887                    currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment;
1888                    return;
1889                }
1890            }
1891
1892            switch (node.kind) {
1893                case SyntaxKind.FunctionExpression:
1894                    if (isArrowFunction(original) || getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
1895                        break;
1896                    }
1897
1898                    // falls through
1899                case SyntaxKind.FunctionDeclaration:
1900                case SyntaxKind.Constructor: {
1901                    const savedClassLexicalEnvironment = currentClassLexicalEnvironment;
1902                    const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment;
1903                    currentClassLexicalEnvironment = undefined;
1904                    currentComputedPropertyNameClassLexicalEnvironment = undefined;
1905                    previousOnEmitNode(hint, node, emitCallback);
1906                    currentClassLexicalEnvironment = savedClassLexicalEnvironment;
1907                    currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment;
1908                    return;
1909                }
1910
1911                case SyntaxKind.GetAccessor:
1912                case SyntaxKind.SetAccessor:
1913                case SyntaxKind.MethodDeclaration:
1914                case SyntaxKind.PropertyDeclaration: {
1915                    const savedClassLexicalEnvironment = currentClassLexicalEnvironment;
1916                    const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment;
1917                    currentComputedPropertyNameClassLexicalEnvironment = currentClassLexicalEnvironment;
1918                    currentClassLexicalEnvironment = undefined;
1919                    previousOnEmitNode(hint, node, emitCallback);
1920                    currentClassLexicalEnvironment = savedClassLexicalEnvironment;
1921                    currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment;
1922                    return;
1923                }
1924                case SyntaxKind.ComputedPropertyName: {
1925                    const savedClassLexicalEnvironment = currentClassLexicalEnvironment;
1926                    const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment;
1927                    currentClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment;
1928                    currentComputedPropertyNameClassLexicalEnvironment = undefined;
1929                    previousOnEmitNode(hint, node, emitCallback);
1930                    currentClassLexicalEnvironment = savedClassLexicalEnvironment;
1931                    currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment;
1932                    return;
1933                }
1934            }
1935            previousOnEmitNode(hint, node, emitCallback);
1936        }
1937
1938        /**
1939         * Hooks node substitutions.
1940         *
1941         * @param hint The context for the emitter.
1942         * @param node The node to substitute.
1943         */
1944        function onSubstituteNode(hint: EmitHint, node: Node) {
1945            node = previousOnSubstituteNode(hint, node);
1946            if (hint === EmitHint.Expression) {
1947                return substituteExpression(node as Expression);
1948            }
1949            return node;
1950        }
1951
1952        function substituteExpression(node: Expression) {
1953            switch (node.kind) {
1954                case SyntaxKind.Identifier:
1955                    return substituteExpressionIdentifier(node as Identifier);
1956                case SyntaxKind.ThisKeyword:
1957                    return substituteThisExpression(node as ThisExpression);
1958            }
1959            return node;
1960        }
1961
1962        function substituteThisExpression(node: ThisExpression) {
1963            if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference && currentClassLexicalEnvironment) {
1964                const { facts, classConstructor } = currentClassLexicalEnvironment;
1965                if (facts & ClassFacts.ClassWasDecorated) {
1966                    return factory.createParenthesizedExpression(factory.createVoidZero());
1967                }
1968                if (classConstructor) {
1969                    return setTextRange(
1970                        setOriginalNode(
1971                            factory.cloneNode(classConstructor),
1972                            node,
1973                        ),
1974                        node
1975                    );
1976                }
1977            }
1978            return node;
1979        }
1980
1981        function substituteExpressionIdentifier(node: Identifier): Expression {
1982            return trySubstituteClassAlias(node) || node;
1983        }
1984
1985        function trySubstituteClassAlias(node: Identifier): Expression | undefined {
1986            if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassAliases) {
1987                if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) {
1988                    // Due to the emit for class decorators, any reference to the class from inside of the class body
1989                    // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
1990                    // behavior of class names in ES6.
1991                    // Also, when emitting statics for class expressions, we must substitute a class alias for
1992                    // constructor references in static property initializers.
1993                    const declaration = resolver.getReferencedValueDeclaration(node);
1994                    if (declaration) {
1995                        const classAlias = classAliases[declaration.id!]; // TODO: GH#18217
1996                        if (classAlias) {
1997                            const clone = factory.cloneNode(classAlias);
1998                            setSourceMapRange(clone, node);
1999                            setCommentRange(clone, node);
2000                            return clone;
2001                        }
2002                    }
2003                }
2004            }
2005
2006            return undefined;
2007        }
2008
2009        /**
2010         * If the name is a computed property, this function transforms it, then either returns an expression which caches the
2011         * value of the result or the expression itself if the value is either unused or safe to inline into multiple locations
2012         * @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator)
2013         */
2014        function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean): Expression | undefined {
2015            if (isComputedPropertyName(name)) {
2016                const expression = visitNode(name.expression, visitor, isExpression);
2017                const innerExpression = skipPartiallyEmittedExpressions(expression);
2018                const inlinable = isSimpleInlineableExpression(innerExpression);
2019                const alreadyTransformed = isAssignmentExpression(innerExpression) && isGeneratedIdentifier(innerExpression.left);
2020                if (!alreadyTransformed && !inlinable && shouldHoist) {
2021                    const generatedName = factory.getGeneratedNameForNode(name);
2022                    if (resolver.getNodeCheckFlags(name) & NodeCheckFlags.BlockScopedBindingInLoop) {
2023                        addBlockScopedVariable(generatedName);
2024                    }
2025                    else {
2026                        hoistVariableDeclaration(generatedName);
2027                    }
2028                    return factory.createAssignment(generatedName, expression);
2029                }
2030                return (inlinable || isIdentifier(innerExpression)) ? undefined : expression;
2031            }
2032        }
2033
2034        function startClassLexicalEnvironment() {
2035            classLexicalEnvironmentStack.push(currentClassLexicalEnvironment);
2036            currentClassLexicalEnvironment = undefined;
2037        }
2038
2039        function endClassLexicalEnvironment() {
2040            currentClassLexicalEnvironment = classLexicalEnvironmentStack.pop();
2041        }
2042
2043        function getClassLexicalEnvironment() {
2044            return currentClassLexicalEnvironment ||= {
2045                facts: ClassFacts.None,
2046                classConstructor: undefined,
2047                superClassReference: undefined,
2048                privateIdentifierEnvironment: undefined,
2049            };
2050        }
2051
2052        function getPrivateIdentifierEnvironment() {
2053            const lex = getClassLexicalEnvironment();
2054            lex.privateIdentifierEnvironment ||= {
2055                className: undefined,
2056                weakSetName: undefined,
2057                identifiers: undefined,
2058                generatedIdentifiers: undefined,
2059            };
2060            return lex.privateIdentifierEnvironment;
2061        }
2062
2063        function getPendingExpressions() {
2064            return pendingExpressions ??= [];
2065        }
2066
2067        function addPrivateIdentifierClassElementToEnvironment(
2068            node: PropertyDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration,
2069            name: PrivateIdentifier,
2070            lex: ClassLexicalEnvironment,
2071            privateEnv: PrivateIdentifierEnvironment,
2072            isStatic: boolean,
2073            isValid: boolean,
2074            previousInfo: PrivateIdentifierInfo | undefined
2075        ) {
2076            if (isAutoAccessorPropertyDeclaration(node)) {
2077                addPrivateIdentifierAutoAccessorPropertyDeclarationToEnvironment(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2078            }
2079            else if (isPropertyDeclaration(node)) {
2080                addPrivateIdentifierPropertyDeclarationToEnvironment(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2081            }
2082            else if (isMethodDeclaration(node)) {
2083                addPrivateIdentifierMethodDeclarationToEnvironment(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2084            }
2085            else if (isGetAccessorDeclaration(node)) {
2086                addPrivateIdentifierGetAccessorDeclarationToEnvironment(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2087            }
2088            else if (isSetAccessorDeclaration(node)) {
2089                addPrivateIdentifierSetAccessorDeclarationToEnvironment(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2090            }
2091        }
2092
2093        function addPrivateIdentifierPropertyDeclarationToEnvironment(
2094            _node: PropertyDeclaration,
2095            name: PrivateIdentifier,
2096            lex: ClassLexicalEnvironment,
2097            privateEnv: PrivateIdentifierEnvironment,
2098            isStatic: boolean,
2099            isValid: boolean,
2100            _previousInfo: PrivateIdentifierInfo | undefined
2101        ) {
2102            if (isStatic) {
2103                Debug.assert(lex.classConstructor, "classConstructor should be set in private identifier environment");
2104
2105                const variableName = createHoistedVariableForPrivateName(name);
2106                setPrivateIdentifier(privateEnv, name, {
2107                    kind: PrivateIdentifierKind.Field,
2108                    brandCheckIdentifier: lex.classConstructor,
2109                    variableName,
2110                    isStatic: true,
2111                    isValid,
2112                });
2113            }
2114            else {
2115                const weakMapName = createHoistedVariableForPrivateName(name);
2116
2117                setPrivateIdentifier(privateEnv, name, {
2118                    kind: PrivateIdentifierKind.Field,
2119                    brandCheckIdentifier: weakMapName,
2120                    variableName: undefined,
2121                    isStatic: false,
2122                    isValid,
2123                });
2124
2125                getPendingExpressions().push(factory.createAssignment(
2126                    weakMapName,
2127                    factory.createNewExpression(
2128                        factory.createIdentifier("WeakMap"),
2129                        /*typeArguments*/ undefined,
2130                        []
2131                    )
2132                ));
2133            }
2134        }
2135
2136        function addPrivateIdentifierMethodDeclarationToEnvironment(
2137            _node: MethodDeclaration,
2138            name: PrivateIdentifier,
2139            lex: ClassLexicalEnvironment,
2140            privateEnv: PrivateIdentifierEnvironment,
2141            isStatic: boolean,
2142            isValid: boolean,
2143            _previousInfo: PrivateIdentifierInfo | undefined
2144        ) {
2145            const methodName = createHoistedVariableForPrivateName(name);
2146            const brandCheckIdentifier = isStatic ?
2147                Debug.checkDefined(lex.classConstructor, "classConstructor should be set in private identifier environment") :
2148                Debug.checkDefined(privateEnv.weakSetName, "weakSetName should be set in private identifier environment");
2149
2150            setPrivateIdentifier(privateEnv, name, {
2151                kind: PrivateIdentifierKind.Method,
2152                methodName,
2153                brandCheckIdentifier,
2154                isStatic,
2155                isValid,
2156            });
2157        }
2158
2159        function addPrivateIdentifierGetAccessorDeclarationToEnvironment(
2160            _node: GetAccessorDeclaration,
2161            name: PrivateIdentifier,
2162            lex: ClassLexicalEnvironment,
2163            privateEnv: PrivateIdentifierEnvironment,
2164            isStatic: boolean,
2165            isValid: boolean,
2166            previousInfo: PrivateIdentifierInfo | undefined
2167        ) {
2168            const getterName = createHoistedVariableForPrivateName(name, "_get");
2169            const brandCheckIdentifier = isStatic ?
2170                Debug.checkDefined(lex.classConstructor, "classConstructor should be set in private identifier environment") :
2171                Debug.checkDefined(privateEnv.weakSetName, "weakSetName should be set in private identifier environment");
2172
2173            if (previousInfo?.kind === PrivateIdentifierKind.Accessor && previousInfo.isStatic === isStatic && !previousInfo.getterName) {
2174                previousInfo.getterName = getterName;
2175            }
2176            else {
2177                setPrivateIdentifier(privateEnv, name, {
2178                    kind: PrivateIdentifierKind.Accessor,
2179                    getterName,
2180                    setterName: undefined,
2181                    brandCheckIdentifier,
2182                    isStatic,
2183                    isValid,
2184                });
2185            }
2186        }
2187
2188        function addPrivateIdentifierSetAccessorDeclarationToEnvironment(
2189            _node: SetAccessorDeclaration,
2190            name: PrivateIdentifier,
2191            lex: ClassLexicalEnvironment,
2192            privateEnv: PrivateIdentifierEnvironment,
2193            isStatic: boolean,
2194            isValid: boolean,
2195            previousInfo: PrivateIdentifierInfo | undefined
2196        ) {
2197            const setterName = createHoistedVariableForPrivateName(name, "_set");
2198            const brandCheckIdentifier = isStatic ?
2199                Debug.checkDefined(lex.classConstructor, "classConstructor should be set in private identifier environment") :
2200                Debug.checkDefined(privateEnv.weakSetName, "weakSetName should be set in private identifier environment");
2201
2202            if (previousInfo?.kind === PrivateIdentifierKind.Accessor && previousInfo.isStatic === isStatic && !previousInfo.setterName) {
2203                previousInfo.setterName = setterName;
2204            }
2205            else {
2206                setPrivateIdentifier(privateEnv, name, {
2207                    kind: PrivateIdentifierKind.Accessor,
2208                    getterName: undefined,
2209                    setterName,
2210                    brandCheckIdentifier,
2211                    isStatic,
2212                    isValid,
2213                });
2214            }
2215        }
2216
2217        function addPrivateIdentifierAutoAccessorPropertyDeclarationToEnvironment(
2218            _node: AutoAccessorPropertyDeclaration,
2219            name: PrivateIdentifier,
2220            lex: ClassLexicalEnvironment,
2221            privateEnv: PrivateIdentifierEnvironment,
2222            isStatic: boolean,
2223            isValid: boolean,
2224            _previousInfo: PrivateIdentifierInfo | undefined
2225        ) {
2226            const getterName = createHoistedVariableForPrivateName(name, "_get");
2227            const setterName = createHoistedVariableForPrivateName(name, "_set");
2228            const brandCheckIdentifier = isStatic ?
2229                Debug.checkDefined(lex.classConstructor, "classConstructor should be set in private identifier environment") :
2230                Debug.checkDefined(privateEnv.weakSetName, "weakSetName should be set in private identifier environment");
2231
2232            setPrivateIdentifier(privateEnv, name, {
2233                kind: PrivateIdentifierKind.Accessor,
2234                getterName,
2235                setterName,
2236                brandCheckIdentifier,
2237                isStatic,
2238                isValid,
2239            });
2240        }
2241
2242        function addPrivateIdentifierToEnvironment<T extends PropertyDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration>(
2243            node: T,
2244            name: PrivateIdentifier,
2245            addDeclaration: (
2246                node: T,
2247                name: PrivateIdentifier,
2248                lex: ClassLexicalEnvironment,
2249                privateEnv: PrivateIdentifierEnvironment,
2250                isStatic: boolean,
2251                isValid: boolean,
2252                previousInfo: PrivateIdentifierInfo | undefined
2253            ) => void
2254        ) {
2255            const lex = getClassLexicalEnvironment();
2256            const privateEnv = getPrivateIdentifierEnvironment();
2257            const previousInfo = getPrivateIdentifier(privateEnv, name);
2258            const isStatic = hasStaticModifier(node);
2259            const isValid = !isReservedPrivateName(name) && previousInfo === undefined;
2260            addDeclaration(node, name, lex, privateEnv, isStatic, isValid, previousInfo);
2261        }
2262
2263        function createHoistedVariableForClass(name: string | PrivateIdentifier | undefined, node: PrivateIdentifier | ClassStaticBlockDeclaration, suffix?: string): Identifier {
2264            const { className } = getPrivateIdentifierEnvironment();
2265            const prefix: GeneratedNamePart | string = className ? { prefix: "_", node: className, suffix: "_" } : "_";
2266            const identifier =
2267                typeof name === "object" ? factory.getGeneratedNameForNode(name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes, prefix, suffix) :
2268                typeof name === "string" ? factory.createUniqueName(name, GeneratedIdentifierFlags.Optimistic, prefix, suffix) :
2269                factory.createTempVariable(/*recordTempVariable*/ undefined, /*reserveInNestedScopes*/ true, prefix, suffix);
2270
2271            if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.BlockScopedBindingInLoop) {
2272                addBlockScopedVariable(identifier);
2273            }
2274            else {
2275                hoistVariableDeclaration(identifier);
2276            }
2277
2278            return identifier;
2279        }
2280
2281        function createHoistedVariableForPrivateName(name: PrivateIdentifier, suffix?: string): Identifier {
2282            const text = tryGetTextOfPropertyName(name) as string | undefined;
2283            return createHoistedVariableForClass(text?.substring(1) ?? name, name, suffix);
2284        }
2285
2286        /**
2287         * Access an already defined {@link PrivateIdentifier} in the current {@link PrivateIdentifierEnvironment}.
2288         *
2289         * @seealso {@link addPrivateIdentifierToEnvironment}
2290         */
2291        function accessPrivateIdentifier(name: PrivateIdentifier) {
2292            if (isGeneratedPrivateIdentifier(name)) {
2293                return accessGeneratedPrivateIdentifier(name);
2294            }
2295            else {
2296                return accessPrivateIdentifierByText(name.escapedText);
2297            }
2298        }
2299
2300        function accessPrivateIdentifierByText(text: __String) {
2301            return accessPrivateIdentifierWorker(getPrivateIdentifierInfo, text);
2302        }
2303
2304        function accessGeneratedPrivateIdentifier(name: GeneratedPrivateIdentifier) {
2305            return accessPrivateIdentifierWorker(getGeneratedPrivateIdentifierInfo, getNodeForGeneratedName(name));
2306        }
2307
2308        function accessPrivateIdentifierWorker<K extends __String | Node>(
2309            getPrivateIdentifierInfo: (privateEnv: PrivateIdentifierEnvironment, key: K) => PrivateIdentifierInfo | undefined,
2310            privateIdentifierKey: K
2311        ) {
2312            if (currentClassLexicalEnvironment?.privateIdentifierEnvironment) {
2313                const info = getPrivateIdentifierInfo(currentClassLexicalEnvironment.privateIdentifierEnvironment, privateIdentifierKey);
2314                if (info) {
2315                    return info;
2316                }
2317            }
2318            for (let i = classLexicalEnvironmentStack.length - 1; i >= 0; --i) {
2319                const env = classLexicalEnvironmentStack[i];
2320                if (!env) {
2321                    continue;
2322                }
2323                if (env.privateIdentifierEnvironment) {
2324                    const info = getPrivateIdentifierInfo(env.privateIdentifierEnvironment, privateIdentifierKey);
2325                    if (info) {
2326                        return info;
2327                    }
2328                }
2329            }
2330            return undefined;
2331        }
2332
2333        function wrapPrivateIdentifierForDestructuringTarget(node: PrivateIdentifierPropertyAccessExpression) {
2334            const parameter = factory.getGeneratedNameForNode(node);
2335            const info = accessPrivateIdentifier(node.name);
2336            if (!info) {
2337                return visitEachChild(node, visitor, context);
2338            }
2339            let receiver = node.expression;
2340            // We cannot copy `this` or `super` into the function because they will be bound
2341            // differently inside the function.
2342            if (isThisProperty(node) || isSuperProperty(node) || !isSimpleCopiableExpression(node.expression)) {
2343                receiver = factory.createTempVariable(hoistVariableDeclaration, /*reservedInNestedScopes*/ true);
2344                getPendingExpressions().push(factory.createBinaryExpression(receiver, SyntaxKind.EqualsToken, visitNode(node.expression, visitor, isExpression)));
2345            }
2346            return factory.createAssignmentTargetWrapper(
2347                parameter,
2348                createPrivateIdentifierAssignment(
2349                    info,
2350                    receiver,
2351                    parameter,
2352                    SyntaxKind.EqualsToken
2353                )
2354            );
2355        }
2356
2357        function visitArrayAssignmentTarget(node: BindingOrAssignmentElement) {
2358            const target = getTargetOfBindingOrAssignmentElement(node);
2359            if (target) {
2360                let wrapped: LeftHandSideExpression | undefined;
2361                if (isPrivateIdentifierPropertyAccessExpression(target)) {
2362                    wrapped = wrapPrivateIdentifierForDestructuringTarget(target);
2363                }
2364                else if (shouldTransformSuperInStaticInitializers &&
2365                    isSuperProperty(target) &&
2366                    currentStaticPropertyDeclarationOrStaticBlock &&
2367                    currentClassLexicalEnvironment) {
2368                    const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
2369                    if (facts & ClassFacts.ClassWasDecorated) {
2370                        wrapped = visitInvalidSuperProperty(target);
2371                    }
2372                    else if (classConstructor && superClassReference) {
2373                        const name =
2374                            isElementAccessExpression(target) ? visitNode(target.argumentExpression, visitor, isExpression) :
2375                            isIdentifier(target.name) ? factory.createStringLiteralFromNode(target.name) :
2376                            undefined;
2377                        if (name) {
2378                            const temp = factory.createTempVariable(/*recordTempVariable*/ undefined);
2379                            wrapped = factory.createAssignmentTargetWrapper(
2380                                temp,
2381                                factory.createReflectSetCall(
2382                                    superClassReference,
2383                                    name,
2384                                    temp,
2385                                    classConstructor,
2386                                )
2387                            );
2388                        }
2389                    }
2390                }
2391                if (wrapped) {
2392                    if (isAssignmentExpression(node)) {
2393                        return factory.updateBinaryExpression(
2394                            node,
2395                            wrapped,
2396                            node.operatorToken,
2397                            visitNode(node.right, visitor, isExpression)
2398                        );
2399                    }
2400                    else if (isSpreadElement(node)) {
2401                        return factory.updateSpreadElement(node, wrapped);
2402                    }
2403                    else {
2404                        return wrapped;
2405                    }
2406                }
2407            }
2408            return visitNode(node, assignmentTargetVisitor);
2409        }
2410
2411        function visitObjectAssignmentTarget(node: ObjectLiteralElementLike) {
2412            if (isObjectBindingOrAssignmentElement(node) && !isShorthandPropertyAssignment(node)) {
2413                const target = getTargetOfBindingOrAssignmentElement(node);
2414                let wrapped: LeftHandSideExpression | undefined;
2415                if (target) {
2416                    if (isPrivateIdentifierPropertyAccessExpression(target)) {
2417                        wrapped = wrapPrivateIdentifierForDestructuringTarget(target);
2418                    }
2419                    else if (shouldTransformSuperInStaticInitializers &&
2420                        isSuperProperty(target) &&
2421                        currentStaticPropertyDeclarationOrStaticBlock &&
2422                        currentClassLexicalEnvironment) {
2423                        const { classConstructor, superClassReference, facts } = currentClassLexicalEnvironment;
2424                        if (facts & ClassFacts.ClassWasDecorated) {
2425                            wrapped = visitInvalidSuperProperty(target);
2426                        }
2427                        else if (classConstructor && superClassReference) {
2428                            const name =
2429                                isElementAccessExpression(target) ? visitNode(target.argumentExpression, visitor, isExpression) :
2430                                isIdentifier(target.name) ? factory.createStringLiteralFromNode(target.name) :
2431                                undefined;
2432                            if (name) {
2433                                const temp = factory.createTempVariable(/*recordTempVariable*/ undefined);
2434                                wrapped = factory.createAssignmentTargetWrapper(
2435                                    temp,
2436                                    factory.createReflectSetCall(
2437                                        superClassReference,
2438                                        name,
2439                                        temp,
2440                                        classConstructor,
2441                                    )
2442                                );
2443                            }
2444                        }
2445                    }
2446                }
2447                if (isPropertyAssignment(node)) {
2448                    const initializer = getInitializerOfBindingOrAssignmentElement(node);
2449                    return factory.updatePropertyAssignment(
2450                        node,
2451                        visitNode(node.name, visitor, isPropertyName),
2452                        wrapped ?
2453                            initializer ? factory.createAssignment(wrapped, visitNode(initializer, visitor)) : wrapped :
2454                            visitNode(node.initializer, assignmentTargetVisitor, isExpression)
2455                    );
2456                }
2457                if (isSpreadAssignment(node)) {
2458                    return factory.updateSpreadAssignment(
2459                        node,
2460                        wrapped || visitNode(node.expression, assignmentTargetVisitor, isExpression)
2461                    );
2462                }
2463                Debug.assert(wrapped === undefined, "Should not have generated a wrapped target");
2464            }
2465            return visitNode(node, visitor);
2466        }
2467
2468        function visitAssignmentPattern(node: AssignmentPattern) {
2469            if (isArrayLiteralExpression(node)) {
2470                // Transforms private names in destructuring assignment array bindings.
2471                // Transforms SuperProperty assignments in destructuring assignment array bindings in static initializers.
2472                //
2473                // Source:
2474                // ([ this.#myProp ] = [ "hello" ]);
2475                //
2476                // Transformation:
2477                // [ { set value(x) { this.#myProp = x; } }.value ] = [ "hello" ];
2478                return factory.updateArrayLiteralExpression(
2479                    node,
2480                    visitNodes(node.elements, visitArrayAssignmentTarget, isExpression)
2481                );
2482            }
2483            else {
2484                // Transforms private names in destructuring assignment object bindings.
2485                // Transforms SuperProperty assignments in destructuring assignment object bindings in static initializers.
2486                //
2487                // Source:
2488                // ({ stringProperty: this.#myProp } = { stringProperty: "hello" });
2489                //
2490                // Transformation:
2491                // ({ stringProperty: { set value(x) { this.#myProp = x; } }.value }) = { stringProperty: "hello" };
2492                return factory.updateObjectLiteralExpression(
2493                    node,
2494                    visitNodes(node.properties, visitObjectAssignmentTarget, isObjectLiteralElementLike)
2495                );
2496            }
2497        }
2498    }
2499
2500    function createPrivateStaticFieldInitializer(variableName: Identifier, initializer: Expression | undefined) {
2501        return factory.createAssignment(
2502            variableName,
2503            factory.createObjectLiteralExpression([
2504                factory.createPropertyAssignment("value", initializer || factory.createVoidZero())
2505            ])
2506        );
2507    }
2508
2509    function createPrivateInstanceFieldInitializer(receiver: LeftHandSideExpression, initializer: Expression | undefined, weakMapName: Identifier) {
2510        return factory.createCallExpression(
2511            factory.createPropertyAccessExpression(weakMapName, "set"),
2512            /*typeArguments*/ undefined,
2513            [receiver, initializer || factory.createVoidZero()]
2514        );
2515    }
2516
2517    function createPrivateInstanceMethodInitializer(receiver: LeftHandSideExpression, weakSetName: Identifier) {
2518        return factory.createCallExpression(
2519            factory.createPropertyAccessExpression(weakSetName, "add"),
2520            /*typeArguments*/ undefined,
2521            [receiver]
2522        );
2523    }
2524
2525    function isReservedPrivateName(node: PrivateIdentifier) {
2526        return !isGeneratedPrivateIdentifier(node) && node.escapedText === "#constructor";
2527    }
2528
2529    function getPrivateIdentifier(privateEnv: PrivateIdentifierEnvironment, name: PrivateIdentifier) {
2530        return isGeneratedPrivateIdentifier(name) ?
2531            getGeneratedPrivateIdentifierInfo(privateEnv, getNodeForGeneratedName(name)) :
2532            getPrivateIdentifierInfo(privateEnv, name.escapedText);
2533    }
2534
2535    function setPrivateIdentifier(privateEnv: PrivateIdentifierEnvironment, name: PrivateIdentifier, info: PrivateIdentifierInfo) {
2536        if (isGeneratedPrivateIdentifier(name)) {
2537            privateEnv.generatedIdentifiers ??= new Map();
2538            privateEnv.generatedIdentifiers.set(getNodeForGeneratedName(name), info);
2539        }
2540        else {
2541            privateEnv.identifiers ??= new Map();
2542            privateEnv.identifiers.set(name.escapedText, info);
2543        }
2544    }
2545
2546    function getPrivateIdentifierInfo(privateEnv: PrivateIdentifierEnvironment, key: __String) {
2547        return privateEnv.identifiers?.get(key);
2548    }
2549
2550    function getGeneratedPrivateIdentifierInfo(privateEnv: PrivateIdentifierEnvironment, key: Node) {
2551        return privateEnv.generatedIdentifiers?.get(key);
2552    }
2553}
2554