1/*@internal*/ 2namespace ts { 3 type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; 4 5 const enum ES2017SubstitutionFlags { 6 /** Enables substitutions for async methods with `super` calls. */ 7 AsyncMethodsWithSuper = 1 << 0 8 } 9 10 const enum ContextFlags { 11 NonTopLevel = 1 << 0, 12 HasLexicalThis = 1 << 1 13 } 14 15 export function transformES2017(context: TransformationContext) { 16 const { 17 factory, 18 getEmitHelperFactory: emitHelpers, 19 resumeLexicalEnvironment, 20 endLexicalEnvironment, 21 hoistVariableDeclaration 22 } = context; 23 24 const resolver = context.getEmitResolver(); 25 const compilerOptions = context.getCompilerOptions(); 26 const languageVersion = getEmitScriptTarget(compilerOptions); 27 28 /** 29 * Keeps track of whether expression substitution has been enabled for specific edge cases. 30 * They are persisted between each SourceFile transformation and should not be reset. 31 */ 32 let enabledSubstitutions: ES2017SubstitutionFlags; 33 34 /** 35 * This keeps track of containers where `super` is valid, for use with 36 * just-in-time substitution for `super` expressions inside of async methods. 37 */ 38 let enclosingSuperContainerFlags: NodeCheckFlags = 0; 39 40 let enclosingFunctionParameterNames: Set<__String>; 41 42 /** 43 * Keeps track of property names accessed on super (`super.x`) within async functions. 44 */ 45 let capturedSuperProperties: Set<__String>; 46 /** Whether the async function contains an element access on super (`super[x]`). */ 47 let hasSuperElementAccess: boolean; 48 /** A set of node IDs for generated super accessors (variable statements). */ 49 const substitutedSuperAccessors: boolean[] = []; 50 51 let contextFlags: ContextFlags = 0; 52 53 // Save the previous transformation hooks. 54 const previousOnEmitNode = context.onEmitNode; 55 const previousOnSubstituteNode = context.onSubstituteNode; 56 57 // Set new transformation hooks. 58 context.onEmitNode = onEmitNode; 59 context.onSubstituteNode = onSubstituteNode; 60 61 return chainBundle(context, transformSourceFile); 62 63 function transformSourceFile(node: SourceFile) { 64 if (node.isDeclarationFile) { 65 return node; 66 } 67 68 setContextFlag(ContextFlags.NonTopLevel, false); 69 setContextFlag(ContextFlags.HasLexicalThis, !isEffectiveStrictModeSourceFile(node, compilerOptions)); 70 const visited = visitEachChild(node, visitor, context); 71 addEmitHelpers(visited, context.readEmitHelpers()); 72 return visited; 73 } 74 75 function setContextFlag(flag: ContextFlags, val: boolean) { 76 contextFlags = val ? contextFlags | flag : contextFlags & ~flag; 77 } 78 79 function inContext(flags: ContextFlags) { 80 return (contextFlags & flags) !== 0; 81 } 82 83 function inTopLevelContext() { 84 return !inContext(ContextFlags.NonTopLevel); 85 } 86 87 function inHasLexicalThisContext() { 88 return inContext(ContextFlags.HasLexicalThis); 89 } 90 91 function doWithContext<T, U>(flags: ContextFlags, cb: (value: T) => U, value: T) { 92 const contextFlagsToSet = flags & ~contextFlags; 93 if (contextFlagsToSet) { 94 setContextFlag(contextFlagsToSet, /*val*/ true); 95 const result = cb(value); 96 setContextFlag(contextFlagsToSet, /*val*/ false); 97 return result; 98 } 99 return cb(value); 100 } 101 102 function visitDefault(node: Node): VisitResult<Node> { 103 return visitEachChild(node, visitor, context); 104 } 105 106 function visitor(node: Node): VisitResult<Node> { 107 if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) { 108 return node; 109 } 110 switch (node.kind) { 111 case SyntaxKind.AsyncKeyword: 112 // ES2017 async modifier should be elided for targets < ES2017 113 return undefined; 114 115 case SyntaxKind.AwaitExpression: 116 return visitAwaitExpression(node as AwaitExpression); 117 118 case SyntaxKind.MethodDeclaration: 119 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitMethodDeclaration, node as MethodDeclaration); 120 121 case SyntaxKind.FunctionDeclaration: 122 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionDeclaration, node as FunctionDeclaration); 123 124 case SyntaxKind.FunctionExpression: 125 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionExpression, node as FunctionExpression); 126 127 case SyntaxKind.ArrowFunction: 128 return doWithContext(ContextFlags.NonTopLevel, visitArrowFunction, node as ArrowFunction); 129 130 case SyntaxKind.PropertyAccessExpression: 131 if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) { 132 capturedSuperProperties.add(node.name.escapedText); 133 } 134 return visitEachChild(node, visitor, context); 135 136 case SyntaxKind.ElementAccessExpression: 137 if (capturedSuperProperties && (node as ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword) { 138 hasSuperElementAccess = true; 139 } 140 return visitEachChild(node, visitor, context); 141 142 case SyntaxKind.GetAccessor: 143 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitGetAccessorDeclaration, node as GetAccessorDeclaration); 144 case SyntaxKind.SetAccessor: 145 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitSetAccessorDeclaration, node as SetAccessorDeclaration); 146 case SyntaxKind.Constructor: 147 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitConstructorDeclaration, node as ConstructorDeclaration); 148 case SyntaxKind.ClassDeclaration: 149 case SyntaxKind.ClassExpression: 150 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node); 151 152 default: 153 return visitEachChild(node, visitor, context); 154 } 155 } 156 157 function asyncBodyVisitor(node: Node): VisitResult<Node> { 158 if (isNodeWithPossibleHoistedDeclaration(node)) { 159 switch (node.kind) { 160 case SyntaxKind.VariableStatement: 161 return visitVariableStatementInAsyncBody(node); 162 case SyntaxKind.ForStatement: 163 return visitForStatementInAsyncBody(node); 164 case SyntaxKind.ForInStatement: 165 return visitForInStatementInAsyncBody(node); 166 case SyntaxKind.ForOfStatement: 167 return visitForOfStatementInAsyncBody(node); 168 case SyntaxKind.CatchClause: 169 return visitCatchClauseInAsyncBody(node); 170 case SyntaxKind.Block: 171 case SyntaxKind.SwitchStatement: 172 case SyntaxKind.CaseBlock: 173 case SyntaxKind.CaseClause: 174 case SyntaxKind.DefaultClause: 175 case SyntaxKind.TryStatement: 176 case SyntaxKind.DoStatement: 177 case SyntaxKind.WhileStatement: 178 case SyntaxKind.IfStatement: 179 case SyntaxKind.WithStatement: 180 case SyntaxKind.LabeledStatement: 181 return visitEachChild(node, asyncBodyVisitor, context); 182 default: 183 return Debug.assertNever(node, "Unhandled node."); 184 } 185 } 186 return visitor(node); 187 } 188 189 function visitCatchClauseInAsyncBody(node: CatchClause) { 190 const catchClauseNames = new Set<__String>(); 191 recordDeclarationName(node.variableDeclaration!, catchClauseNames); // TODO: GH#18217 192 193 // names declared in a catch variable are block scoped 194 let catchClauseUnshadowedNames: Set<__String> | undefined; 195 catchClauseNames.forEach((_, escapedName) => { 196 if (enclosingFunctionParameterNames.has(escapedName)) { 197 if (!catchClauseUnshadowedNames) { 198 catchClauseUnshadowedNames = new Set(enclosingFunctionParameterNames); 199 } 200 catchClauseUnshadowedNames.delete(escapedName); 201 } 202 }); 203 204 if (catchClauseUnshadowedNames) { 205 const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames; 206 enclosingFunctionParameterNames = catchClauseUnshadowedNames; 207 const result = visitEachChild(node, asyncBodyVisitor, context); 208 enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames; 209 return result; 210 } 211 else { 212 return visitEachChild(node, asyncBodyVisitor, context); 213 } 214 } 215 216 function visitVariableStatementInAsyncBody(node: VariableStatement) { 217 if (isVariableDeclarationListWithCollidingName(node.declarationList)) { 218 const expression = visitVariableDeclarationListWithCollidingNames(node.declarationList, /*hasReceiver*/ false); 219 return expression ? factory.createExpressionStatement(expression) : undefined; 220 } 221 return visitEachChild(node, visitor, context); 222 } 223 224 function visitForInStatementInAsyncBody(node: ForInStatement) { 225 return factory.updateForInStatement( 226 node, 227 isVariableDeclarationListWithCollidingName(node.initializer) 228 ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! 229 : visitNode(node.initializer, visitor, isForInitializer), 230 visitNode(node.expression, visitor, isExpression), 231 visitIterationBody(node.statement, asyncBodyVisitor, context) 232 ); 233 } 234 235 function visitForOfStatementInAsyncBody(node: ForOfStatement) { 236 return factory.updateForOfStatement( 237 node, 238 visitNode(node.awaitModifier, visitor, isToken), 239 isVariableDeclarationListWithCollidingName(node.initializer) 240 ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! 241 : visitNode(node.initializer, visitor, isForInitializer), 242 visitNode(node.expression, visitor, isExpression), 243 visitIterationBody(node.statement, asyncBodyVisitor, context) 244 ); 245 } 246 247 function visitForStatementInAsyncBody(node: ForStatement) { 248 const initializer = node.initializer!; // TODO: GH#18217 249 return factory.updateForStatement( 250 node, 251 isVariableDeclarationListWithCollidingName(initializer) 252 ? visitVariableDeclarationListWithCollidingNames(initializer, /*hasReceiver*/ false) 253 : visitNode(node.initializer, visitor, isForInitializer), 254 visitNode(node.condition, visitor, isExpression), 255 visitNode(node.incrementor, visitor, isExpression), 256 visitIterationBody(node.statement, asyncBodyVisitor, context) 257 ); 258 } 259 260 /** 261 * Visits an AwaitExpression node. 262 * 263 * This function will be called any time a ES2017 await expression is encountered. 264 * 265 * @param node The node to visit. 266 */ 267 function visitAwaitExpression(node: AwaitExpression): Expression { 268 // do not downlevel a top-level await as it is module syntax... 269 if (inTopLevelContext()) { 270 return visitEachChild(node, visitor, context); 271 } 272 return setOriginalNode( 273 setTextRange( 274 factory.createYieldExpression( 275 /*asteriskToken*/ undefined, 276 visitNode(node.expression, visitor, isExpression) 277 ), 278 node 279 ), 280 node 281 ); 282 } 283 284 function visitConstructorDeclaration(node: ConstructorDeclaration) { 285 return factory.updateConstructorDeclaration( 286 node, 287 visitNodes(node.modifiers, visitor, isModifierLike), 288 visitParameterList(node.parameters, visitor, context), 289 transformMethodBody(node) 290 ); 291 } 292 293 /** 294 * Visits a MethodDeclaration node. 295 * 296 * This function will be called when one of the following conditions are met: 297 * - The node is marked as async 298 * 299 * @param node The node to visit. 300 */ 301 function visitMethodDeclaration(node: MethodDeclaration) { 302 return factory.updateMethodDeclaration( 303 node, 304 visitNodes(node.modifiers, visitor, isModifierLike), 305 node.asteriskToken, 306 node.name, 307 /*questionToken*/ undefined, 308 /*typeParameters*/ undefined, 309 visitParameterList(node.parameters, visitor, context), 310 /*type*/ undefined, 311 getFunctionFlags(node) & FunctionFlags.Async 312 ? transformAsyncFunctionBody(node) 313 : transformMethodBody(node) 314 ); 315 } 316 317 function visitGetAccessorDeclaration(node: GetAccessorDeclaration) { 318 return factory.updateGetAccessorDeclaration( 319 node, 320 visitNodes(node.modifiers, visitor, isModifierLike), 321 node.name, 322 visitParameterList(node.parameters, visitor, context), 323 /*type*/ undefined, 324 transformMethodBody(node) 325 ); 326 } 327 328 function visitSetAccessorDeclaration(node: SetAccessorDeclaration) { 329 return factory.updateSetAccessorDeclaration( 330 node, 331 visitNodes(node.modifiers, visitor, isModifierLike), 332 node.name, 333 visitParameterList(node.parameters, visitor, context), 334 transformMethodBody(node) 335 ); 336 } 337 338 /** 339 * Visits a FunctionDeclaration node. 340 * 341 * This function will be called when one of the following conditions are met: 342 * - The node is marked async 343 * 344 * @param node The node to visit. 345 */ 346 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 347 return factory.updateFunctionDeclaration( 348 node, 349 visitNodes(node.modifiers, visitor, isModifierLike), 350 node.asteriskToken, 351 node.name, 352 /*typeParameters*/ undefined, 353 visitParameterList(node.parameters, visitor, context), 354 /*type*/ undefined, 355 getFunctionFlags(node) & FunctionFlags.Async 356 ? transformAsyncFunctionBody(node) 357 : visitFunctionBody(node.body, visitor, context) 358 ); 359 } 360 361 /** 362 * Visits a FunctionExpression node. 363 * 364 * This function will be called when one of the following conditions are met: 365 * - The node is marked async 366 * 367 * @param node The node to visit. 368 */ 369 function visitFunctionExpression(node: FunctionExpression): Expression { 370 return factory.updateFunctionExpression( 371 node, 372 visitNodes(node.modifiers, visitor, isModifierLike), 373 node.asteriskToken, 374 node.name, 375 /*typeParameters*/ undefined, 376 visitParameterList(node.parameters, visitor, context), 377 /*type*/ undefined, 378 getFunctionFlags(node) & FunctionFlags.Async 379 ? transformAsyncFunctionBody(node) 380 : visitFunctionBody(node.body, visitor, context) 381 ); 382 } 383 384 /** 385 * Visits an ArrowFunction. 386 * 387 * This function will be called when one of the following conditions are met: 388 * - The node is marked async 389 * 390 * @param node The node to visit. 391 */ 392 function visitArrowFunction(node: ArrowFunction) { 393 return factory.updateArrowFunction( 394 node, 395 visitNodes(node.modifiers, visitor, isModifierLike), 396 /*typeParameters*/ undefined, 397 visitParameterList(node.parameters, visitor, context), 398 /*type*/ undefined, 399 node.equalsGreaterThanToken, 400 getFunctionFlags(node) & FunctionFlags.Async 401 ? transformAsyncFunctionBody(node) 402 : visitFunctionBody(node.body, visitor, context), 403 ); 404 } 405 406 function recordDeclarationName({ name }: ParameterDeclaration | VariableDeclaration | BindingElement, names: Set<__String>) { 407 if (isIdentifier(name)) { 408 names.add(name.escapedText); 409 } 410 else { 411 for (const element of name.elements) { 412 if (!isOmittedExpression(element)) { 413 recordDeclarationName(element, names); 414 } 415 } 416 } 417 } 418 419 function isVariableDeclarationListWithCollidingName(node: ForInitializer): node is VariableDeclarationList { 420 return !!node 421 && isVariableDeclarationList(node) 422 && !(node.flags & NodeFlags.BlockScoped) 423 && node.declarations.some(collidesWithParameterName); 424 } 425 426 function visitVariableDeclarationListWithCollidingNames(node: VariableDeclarationList, hasReceiver: boolean) { 427 hoistVariableDeclarationList(node); 428 429 const variables = getInitializedVariables(node); 430 if (variables.length === 0) { 431 if (hasReceiver) { 432 return visitNode(factory.converters.convertToAssignmentElementTarget(node.declarations[0].name), visitor, isExpression); 433 } 434 return undefined; 435 } 436 437 return factory.inlineExpressions(map(variables, transformInitializedVariable)); 438 } 439 440 function hoistVariableDeclarationList(node: VariableDeclarationList) { 441 forEach(node.declarations, hoistVariable); 442 } 443 444 function hoistVariable({ name }: VariableDeclaration | BindingElement) { 445 if (isIdentifier(name)) { 446 hoistVariableDeclaration(name); 447 } 448 else { 449 for (const element of name.elements) { 450 if (!isOmittedExpression(element)) { 451 hoistVariable(element); 452 } 453 } 454 } 455 } 456 457 function transformInitializedVariable(node: VariableDeclaration) { 458 const converted = setSourceMapRange( 459 factory.createAssignment( 460 factory.converters.convertToAssignmentElementTarget(node.name), 461 node.initializer! 462 ), 463 node 464 ); 465 return visitNode(converted, visitor, isExpression); 466 } 467 468 function collidesWithParameterName({ name }: VariableDeclaration | BindingElement): boolean { 469 if (isIdentifier(name)) { 470 return enclosingFunctionParameterNames.has(name.escapedText); 471 } 472 else { 473 for (const element of name.elements) { 474 if (!isOmittedExpression(element) && collidesWithParameterName(element)) { 475 return true; 476 } 477 } 478 } 479 return false; 480 } 481 482 function transformMethodBody(node: MethodDeclaration | AccessorDeclaration | ConstructorDeclaration): FunctionBody | undefined { 483 Debug.assertIsDefined(node.body); 484 485 const savedCapturedSuperProperties = capturedSuperProperties; 486 const savedHasSuperElementAccess = hasSuperElementAccess; 487 capturedSuperProperties = new Set(); 488 hasSuperElementAccess = false; 489 490 let updated = visitFunctionBody(node.body, visitor, context); 491 492 // Minor optimization, emit `_super` helper to capture `super` access in an arrow. 493 // This step isn't needed if we eventually transform this to ES5. 494 const originalMethod = getOriginalNode(node, isFunctionLikeDeclaration); 495 const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && 496 resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) && 497 (getFunctionFlags(originalMethod) & FunctionFlags.AsyncGenerator) !== FunctionFlags.AsyncGenerator; 498 499 if (emitSuperHelpers) { 500 enableSubstitutionForAsyncMethodsWithSuper(); 501 if (capturedSuperProperties.size) { 502 const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); 503 substitutedSuperAccessors[getNodeId(variableStatement)] = true; 504 505 const statements = updated.statements.slice(); 506 insertStatementsAfterStandardPrologue(statements, [variableStatement]); 507 updated = factory.updateBlock(updated, statements); 508 } 509 510 if (hasSuperElementAccess) { 511 // Emit helpers for super element access expressions (`super[x]`). 512 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) { 513 addEmitHelper(updated, advancedAsyncSuperHelper); 514 } 515 else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) { 516 addEmitHelper(updated, asyncSuperHelper); 517 } 518 } 519 } 520 521 capturedSuperProperties = savedCapturedSuperProperties; 522 hasSuperElementAccess = savedHasSuperElementAccess; 523 return updated; 524 } 525 526 function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; 527 function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody; 528 function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody { 529 resumeLexicalEnvironment(); 530 531 const original = getOriginalNode(node, isFunctionLike); 532 const nodeType = original.type; 533 const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; 534 const isArrowFunction = node.kind === SyntaxKind.ArrowFunction; 535 const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; 536 537 // An async function is emit as an outer function that calls an inner 538 // generator function. To preserve lexical bindings, we pass the current 539 // `this` and `arguments` objects to `__awaiter`. The generator function 540 // passed to `__awaiter` is executed inside of the callback to the 541 // promise constructor. 542 543 const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames; 544 enclosingFunctionParameterNames = new Set(); 545 for (const parameter of node.parameters) { 546 recordDeclarationName(parameter, enclosingFunctionParameterNames); 547 } 548 549 const savedCapturedSuperProperties = capturedSuperProperties; 550 const savedHasSuperElementAccess = hasSuperElementAccess; 551 if (!isArrowFunction) { 552 capturedSuperProperties = new Set(); 553 hasSuperElementAccess = false; 554 } 555 556 let result: ConciseBody; 557 if (!isArrowFunction) { 558 const statements: Statement[] = []; 559 const statementOffset = factory.copyPrologue((node.body as Block).statements, statements, /*ensureUseStrict*/ false, visitor); 560 statements.push( 561 factory.createReturnStatement( 562 emitHelpers().createAwaiterHelper( 563 inHasLexicalThisContext(), 564 hasLexicalArguments, 565 promiseConstructor, 566 transformAsyncFunctionBodyWorker(node.body as Block, statementOffset) 567 ) 568 ) 569 ); 570 571 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 572 573 // Minor optimization, emit `_super` helper to capture `super` access in an arrow. 574 // This step isn't needed if we eventually transform this to ES5. 575 const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync); 576 577 if (emitSuperHelpers) { 578 enableSubstitutionForAsyncMethodsWithSuper(); 579 if (capturedSuperProperties.size) { 580 const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); 581 substitutedSuperAccessors[getNodeId(variableStatement)] = true; 582 insertStatementsAfterStandardPrologue(statements, [variableStatement]); 583 } 584 } 585 586 const block = factory.createBlock(statements, /*multiLine*/ true); 587 setTextRange(block, node.body); 588 589 if (emitSuperHelpers && hasSuperElementAccess) { 590 // Emit helpers for super element access expressions (`super[x]`). 591 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) { 592 addEmitHelper(block, advancedAsyncSuperHelper); 593 } 594 else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) { 595 addEmitHelper(block, asyncSuperHelper); 596 } 597 } 598 599 result = block; 600 } 601 else { 602 const expression = emitHelpers().createAwaiterHelper( 603 inHasLexicalThisContext(), 604 hasLexicalArguments, 605 promiseConstructor, 606 transformAsyncFunctionBodyWorker(node.body) 607 ); 608 609 const declarations = endLexicalEnvironment(); 610 if (some(declarations)) { 611 const block = factory.converters.convertToFunctionBlock(expression); 612 result = factory.updateBlock(block, setTextRange(factory.createNodeArray(concatenate(declarations, block.statements)), block.statements)); 613 } 614 else { 615 result = expression; 616 } 617 } 618 619 enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames; 620 if (!isArrowFunction) { 621 capturedSuperProperties = savedCapturedSuperProperties; 622 hasSuperElementAccess = savedHasSuperElementAccess; 623 } 624 return result; 625 } 626 627 function transformAsyncFunctionBodyWorker(body: ConciseBody, start?: number) { 628 if (isBlock(body)) { 629 return factory.updateBlock(body, visitNodes(body.statements, asyncBodyVisitor, isStatement, start)); 630 } 631 else { 632 return factory.converters.convertToFunctionBlock(visitNode(body, asyncBodyVisitor, isConciseBody)); 633 } 634 } 635 636 function getPromiseConstructor(type: TypeNode | undefined) { 637 const typeName = type && getEntityNameFromTypeNode(type); 638 if (typeName && isEntityName(typeName)) { 639 const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); 640 if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue 641 || serializationKind === TypeReferenceSerializationKind.Unknown) { 642 return typeName; 643 } 644 } 645 646 return undefined; 647 } 648 649 function enableSubstitutionForAsyncMethodsWithSuper() { 650 if ((enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper) === 0) { 651 enabledSubstitutions |= ES2017SubstitutionFlags.AsyncMethodsWithSuper; 652 653 // We need to enable substitutions for call, property access, and element access 654 // if we need to rewrite super calls. 655 context.enableSubstitution(SyntaxKind.CallExpression); 656 context.enableSubstitution(SyntaxKind.PropertyAccessExpression); 657 context.enableSubstitution(SyntaxKind.ElementAccessExpression); 658 659 // We need to be notified when entering and exiting declarations that bind super. 660 context.enableEmitNotification(SyntaxKind.ClassDeclaration); 661 context.enableEmitNotification(SyntaxKind.MethodDeclaration); 662 context.enableEmitNotification(SyntaxKind.GetAccessor); 663 context.enableEmitNotification(SyntaxKind.SetAccessor); 664 context.enableEmitNotification(SyntaxKind.Constructor); 665 // We need to be notified when entering the generated accessor arrow functions. 666 context.enableEmitNotification(SyntaxKind.VariableStatement); 667 } 668 } 669 670 /** 671 * Hook for node emit. 672 * 673 * @param hint A hint as to the intended usage of the node. 674 * @param node The node to emit. 675 * @param emit A callback used to emit the node in the printer. 676 */ 677 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 678 // If we need to support substitutions for `super` in an async method, 679 // we should track it here. 680 if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { 681 const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync); 682 if (superContainerFlags !== enclosingSuperContainerFlags) { 683 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 684 enclosingSuperContainerFlags = superContainerFlags; 685 previousOnEmitNode(hint, node, emitCallback); 686 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 687 return; 688 } 689 } 690 // Disable substitution in the generated super accessor itself. 691 else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { 692 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 693 enclosingSuperContainerFlags = 0; 694 previousOnEmitNode(hint, node, emitCallback); 695 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 696 return; 697 } 698 previousOnEmitNode(hint, node, emitCallback); 699 } 700 701 /** 702 * Hooks node substitutions. 703 * 704 * @param hint A hint as to the intended usage of the node. 705 * @param node The node to substitute. 706 */ 707 function onSubstituteNode(hint: EmitHint, node: Node) { 708 node = previousOnSubstituteNode(hint, node); 709 if (hint === EmitHint.Expression && enclosingSuperContainerFlags) { 710 return substituteExpression(node as Expression); 711 } 712 713 return node; 714 } 715 716 function substituteExpression(node: Expression) { 717 switch (node.kind) { 718 case SyntaxKind.PropertyAccessExpression: 719 return substitutePropertyAccessExpression(node as PropertyAccessExpression); 720 case SyntaxKind.ElementAccessExpression: 721 return substituteElementAccessExpression(node as ElementAccessExpression); 722 case SyntaxKind.CallExpression: 723 return substituteCallExpression(node as CallExpression); 724 } 725 return node; 726 } 727 728 function substitutePropertyAccessExpression(node: PropertyAccessExpression) { 729 if (node.expression.kind === SyntaxKind.SuperKeyword) { 730 return setTextRange( 731 factory.createPropertyAccessExpression( 732 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 733 node.name), 734 node 735 ); 736 } 737 return node; 738 } 739 740 function substituteElementAccessExpression(node: ElementAccessExpression) { 741 if (node.expression.kind === SyntaxKind.SuperKeyword) { 742 return createSuperElementAccessInAsyncMethod( 743 node.argumentExpression, 744 node 745 ); 746 } 747 return node; 748 } 749 750 function substituteCallExpression(node: CallExpression): Expression { 751 const expression = node.expression; 752 if (isSuperProperty(expression)) { 753 const argumentExpression = isPropertyAccessExpression(expression) 754 ? substitutePropertyAccessExpression(expression) 755 : substituteElementAccessExpression(expression); 756 return factory.createCallExpression( 757 factory.createPropertyAccessExpression(argumentExpression, "call"), 758 /*typeArguments*/ undefined, 759 [ 760 factory.createThis(), 761 ...node.arguments 762 ] 763 ); 764 } 765 return node; 766 } 767 768 function isSuperContainer(node: Node): node is SuperContainer { 769 const kind = node.kind; 770 return kind === SyntaxKind.ClassDeclaration 771 || kind === SyntaxKind.Constructor 772 || kind === SyntaxKind.MethodDeclaration 773 || kind === SyntaxKind.GetAccessor 774 || kind === SyntaxKind.SetAccessor; 775 } 776 777 function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression { 778 if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) { 779 return setTextRange( 780 factory.createPropertyAccessExpression( 781 factory.createCallExpression( 782 factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 783 /*typeArguments*/ undefined, 784 [argumentExpression] 785 ), 786 "value" 787 ), 788 location 789 ); 790 } 791 else { 792 return setTextRange( 793 factory.createCallExpression( 794 factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 795 /*typeArguments*/ undefined, 796 [argumentExpression] 797 ), 798 location 799 ); 800 } 801 } 802 } 803 804 /** Creates a variable named `_super` with accessor properties for the given property names. */ 805 export function createSuperAccessVariableStatement(factory: NodeFactory, resolver: EmitResolver, node: FunctionLikeDeclaration, names: Set<__String>) { 806 // Create a variable declaration with a getter/setter (if binding) definition for each name: 807 // const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... }); 808 const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) !== 0; 809 const accessors: PropertyAssignment[] = []; 810 names.forEach((_, key) => { 811 const name = unescapeLeadingUnderscores(key); 812 const getterAndSetter: PropertyAssignment[] = []; 813 getterAndSetter.push(factory.createPropertyAssignment( 814 "get", 815 factory.createArrowFunction( 816 /* modifiers */ undefined, 817 /* typeParameters */ undefined, 818 /* parameters */ [], 819 /* type */ undefined, 820 /* equalsGreaterThanToken */ undefined, 821 setEmitFlags( 822 factory.createPropertyAccessExpression( 823 setEmitFlags( 824 factory.createSuper(), 825 EmitFlags.NoSubstitution 826 ), 827 name 828 ), 829 EmitFlags.NoSubstitution 830 ) 831 ) 832 )); 833 if (hasBinding) { 834 getterAndSetter.push( 835 factory.createPropertyAssignment( 836 "set", 837 factory.createArrowFunction( 838 /* modifiers */ undefined, 839 /* typeParameters */ undefined, 840 /* parameters */ [ 841 factory.createParameterDeclaration( 842 /* modifiers */ undefined, 843 /* dotDotDotToken */ undefined, 844 "v", 845 /* questionToken */ undefined, 846 /* type */ undefined, 847 /* initializer */ undefined 848 ) 849 ], 850 /* type */ undefined, 851 /* equalsGreaterThanToken */ undefined, 852 factory.createAssignment( 853 setEmitFlags( 854 factory.createPropertyAccessExpression( 855 setEmitFlags( 856 factory.createSuper(), 857 EmitFlags.NoSubstitution 858 ), 859 name 860 ), 861 EmitFlags.NoSubstitution 862 ), 863 factory.createIdentifier("v") 864 ) 865 ) 866 ) 867 ); 868 } 869 accessors.push( 870 factory.createPropertyAssignment( 871 name, 872 factory.createObjectLiteralExpression(getterAndSetter), 873 ) 874 ); 875 }); 876 return factory.createVariableStatement( 877 /* modifiers */ undefined, 878 factory.createVariableDeclarationList( 879 [ 880 factory.createVariableDeclaration( 881 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 882 /*exclamationToken*/ undefined, 883 /* type */ undefined, 884 factory.createCallExpression( 885 factory.createPropertyAccessExpression( 886 factory.createIdentifier("Object"), 887 "create" 888 ), 889 /* typeArguments */ undefined, 890 [ 891 factory.createNull(), 892 factory.createObjectLiteralExpression(accessors, /* multiline */ true) 893 ] 894 ) 895 ) 896 ], 897 NodeFlags.Const)); 898 } 899} 900