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