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