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