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