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