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