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(<AwaitExpression>node); 117 118 case SyntaxKind.MethodDeclaration: 119 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitMethodDeclaration, <MethodDeclaration>node); 120 121 case SyntaxKind.FunctionDeclaration: 122 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionDeclaration, <FunctionDeclaration>node); 123 124 case SyntaxKind.FunctionExpression: 125 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionExpression, <FunctionExpression>node); 126 127 case SyntaxKind.ArrowFunction: 128 return doWithContext(ContextFlags.NonTopLevel, visitArrowFunction, <ArrowFunction>node); 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 && (<ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword) { 138 hasSuperElementAccess = true; 139 } 140 return visitEachChild(node, visitor, context); 141 142 case SyntaxKind.GetAccessor: 143 case SyntaxKind.SetAccessor: 144 case SyntaxKind.Constructor: 145 case SyntaxKind.ClassDeclaration: 146 case SyntaxKind.ClassExpression: 147 return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node); 148 149 default: 150 return visitEachChild(node, visitor, context); 151 } 152 } 153 154 function asyncBodyVisitor(node: Node): VisitResult<Node> { 155 if (isNodeWithPossibleHoistedDeclaration(node)) { 156 switch (node.kind) { 157 case SyntaxKind.VariableStatement: 158 return visitVariableStatementInAsyncBody(node); 159 case SyntaxKind.ForStatement: 160 return visitForStatementInAsyncBody(node); 161 case SyntaxKind.ForInStatement: 162 return visitForInStatementInAsyncBody(node); 163 case SyntaxKind.ForOfStatement: 164 return visitForOfStatementInAsyncBody(node); 165 case SyntaxKind.CatchClause: 166 return visitCatchClauseInAsyncBody(node); 167 case SyntaxKind.Block: 168 case SyntaxKind.SwitchStatement: 169 case SyntaxKind.CaseBlock: 170 case SyntaxKind.CaseClause: 171 case SyntaxKind.DefaultClause: 172 case SyntaxKind.TryStatement: 173 case SyntaxKind.DoStatement: 174 case SyntaxKind.WhileStatement: 175 case SyntaxKind.IfStatement: 176 case SyntaxKind.WithStatement: 177 case SyntaxKind.LabeledStatement: 178 return visitEachChild(node, asyncBodyVisitor, context); 179 default: 180 return Debug.assertNever(node, "Unhandled node."); 181 } 182 } 183 return visitor(node); 184 } 185 186 function visitCatchClauseInAsyncBody(node: CatchClause) { 187 const catchClauseNames = new Set<__String>(); 188 recordDeclarationName(node.variableDeclaration!, catchClauseNames); // TODO: GH#18217 189 190 // names declared in a catch variable are block scoped 191 let catchClauseUnshadowedNames: Set<__String> | undefined; 192 catchClauseNames.forEach((_, escapedName) => { 193 if (enclosingFunctionParameterNames.has(escapedName)) { 194 if (!catchClauseUnshadowedNames) { 195 catchClauseUnshadowedNames = new Set(enclosingFunctionParameterNames); 196 } 197 catchClauseUnshadowedNames.delete(escapedName); 198 } 199 }); 200 201 if (catchClauseUnshadowedNames) { 202 const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames; 203 enclosingFunctionParameterNames = catchClauseUnshadowedNames; 204 const result = visitEachChild(node, asyncBodyVisitor, context); 205 enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames; 206 return result; 207 } 208 else { 209 return visitEachChild(node, asyncBodyVisitor, context); 210 } 211 } 212 213 function visitVariableStatementInAsyncBody(node: VariableStatement) { 214 if (isVariableDeclarationListWithCollidingName(node.declarationList)) { 215 const expression = visitVariableDeclarationListWithCollidingNames(node.declarationList, /*hasReceiver*/ false); 216 return expression ? factory.createExpressionStatement(expression) : undefined; 217 } 218 return visitEachChild(node, visitor, context); 219 } 220 221 function visitForInStatementInAsyncBody(node: ForInStatement) { 222 return factory.updateForInStatement( 223 node, 224 isVariableDeclarationListWithCollidingName(node.initializer) 225 ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! 226 : visitNode(node.initializer, visitor, isForInitializer), 227 visitNode(node.expression, visitor, isExpression), 228 visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) 229 ); 230 } 231 232 function visitForOfStatementInAsyncBody(node: ForOfStatement) { 233 return factory.updateForOfStatement( 234 node, 235 visitNode(node.awaitModifier, visitor, isToken), 236 isVariableDeclarationListWithCollidingName(node.initializer) 237 ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! 238 : visitNode(node.initializer, visitor, isForInitializer), 239 visitNode(node.expression, visitor, isExpression), 240 visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) 241 ); 242 } 243 244 function visitForStatementInAsyncBody(node: ForStatement) { 245 const initializer = node.initializer!; // TODO: GH#18217 246 return factory.updateForStatement( 247 node, 248 isVariableDeclarationListWithCollidingName(initializer) 249 ? visitVariableDeclarationListWithCollidingNames(initializer, /*hasReceiver*/ false) 250 : visitNode(node.initializer, visitor, isForInitializer), 251 visitNode(node.condition, visitor, isExpression), 252 visitNode(node.incrementor, visitor, isExpression), 253 visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) 254 ); 255 } 256 257 /** 258 * Visits an AwaitExpression node. 259 * 260 * This function will be called any time a ES2017 await expression is encountered. 261 * 262 * @param node The node to visit. 263 */ 264 function visitAwaitExpression(node: AwaitExpression): Expression { 265 // do not downlevel a top-level await as it is module syntax... 266 if (inTopLevelContext()) { 267 return visitEachChild(node, visitor, context); 268 } 269 return setOriginalNode( 270 setTextRange( 271 factory.createYieldExpression( 272 /*asteriskToken*/ undefined, 273 visitNode(node.expression, visitor, isExpression) 274 ), 275 node 276 ), 277 node 278 ); 279 } 280 281 /** 282 * Visits a MethodDeclaration node. 283 * 284 * This function will be called when one of the following conditions are met: 285 * - The node is marked as async 286 * 287 * @param node The node to visit. 288 */ 289 function visitMethodDeclaration(node: MethodDeclaration) { 290 return factory.updateMethodDeclaration( 291 node, 292 /*decorators*/ undefined, 293 visitNodes(node.modifiers, visitor, isModifier), 294 node.asteriskToken, 295 node.name, 296 /*questionToken*/ undefined, 297 /*typeParameters*/ undefined, 298 visitParameterList(node.parameters, visitor, context), 299 /*type*/ undefined, 300 getFunctionFlags(node) & FunctionFlags.Async 301 ? transformAsyncFunctionBody(node) 302 : visitFunctionBody(node.body, visitor, context) 303 ); 304 } 305 306 /** 307 * Visits a FunctionDeclaration node. 308 * 309 * This function will be called when one of the following conditions are met: 310 * - The node is marked async 311 * 312 * @param node The node to visit. 313 */ 314 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 315 return factory.updateFunctionDeclaration( 316 node, 317 /*decorators*/ undefined, 318 visitNodes(node.modifiers, visitor, isModifier), 319 node.asteriskToken, 320 node.name, 321 /*typeParameters*/ undefined, 322 visitParameterList(node.parameters, visitor, context), 323 /*type*/ undefined, 324 getFunctionFlags(node) & FunctionFlags.Async 325 ? transformAsyncFunctionBody(node) 326 : visitFunctionBody(node.body, visitor, context) 327 ); 328 } 329 330 /** 331 * Visits a FunctionExpression node. 332 * 333 * This function will be called when one of the following conditions are met: 334 * - The node is marked async 335 * 336 * @param node The node to visit. 337 */ 338 function visitFunctionExpression(node: FunctionExpression): Expression { 339 return factory.updateFunctionExpression( 340 node, 341 visitNodes(node.modifiers, visitor, isModifier), 342 node.asteriskToken, 343 node.name, 344 /*typeParameters*/ undefined, 345 visitParameterList(node.parameters, visitor, context), 346 /*type*/ undefined, 347 getFunctionFlags(node) & FunctionFlags.Async 348 ? transformAsyncFunctionBody(node) 349 : visitFunctionBody(node.body, visitor, context) 350 ); 351 } 352 353 /** 354 * Visits an ArrowFunction. 355 * 356 * This function will be called when one of the following conditions are met: 357 * - The node is marked async 358 * 359 * @param node The node to visit. 360 */ 361 function visitArrowFunction(node: ArrowFunction) { 362 return factory.updateArrowFunction( 363 node, 364 visitNodes(node.modifiers, visitor, isModifier), 365 /*typeParameters*/ undefined, 366 visitParameterList(node.parameters, visitor, context), 367 /*type*/ undefined, 368 node.equalsGreaterThanToken, 369 getFunctionFlags(node) & FunctionFlags.Async 370 ? transformAsyncFunctionBody(node) 371 : visitFunctionBody(node.body, visitor, context), 372 ); 373 } 374 375 function recordDeclarationName({ name }: ParameterDeclaration | VariableDeclaration | BindingElement, names: Set<__String>) { 376 if (isIdentifier(name)) { 377 names.add(name.escapedText); 378 } 379 else { 380 for (const element of name.elements) { 381 if (!isOmittedExpression(element)) { 382 recordDeclarationName(element, names); 383 } 384 } 385 } 386 } 387 388 function isVariableDeclarationListWithCollidingName(node: ForInitializer): node is VariableDeclarationList { 389 return !!node 390 && isVariableDeclarationList(node) 391 && !(node.flags & NodeFlags.BlockScoped) 392 && node.declarations.some(collidesWithParameterName); 393 } 394 395 function visitVariableDeclarationListWithCollidingNames(node: VariableDeclarationList, hasReceiver: boolean) { 396 hoistVariableDeclarationList(node); 397 398 const variables = getInitializedVariables(node); 399 if (variables.length === 0) { 400 if (hasReceiver) { 401 return visitNode(factory.converters.convertToAssignmentElementTarget(node.declarations[0].name), visitor, isExpression); 402 } 403 return undefined; 404 } 405 406 return factory.inlineExpressions(map(variables, transformInitializedVariable)); 407 } 408 409 function hoistVariableDeclarationList(node: VariableDeclarationList) { 410 forEach(node.declarations, hoistVariable); 411 } 412 413 function hoistVariable({ name }: VariableDeclaration | BindingElement) { 414 if (isIdentifier(name)) { 415 hoistVariableDeclaration(name); 416 } 417 else { 418 for (const element of name.elements) { 419 if (!isOmittedExpression(element)) { 420 hoistVariable(element); 421 } 422 } 423 } 424 } 425 426 function transformInitializedVariable(node: VariableDeclaration) { 427 const converted = setSourceMapRange( 428 factory.createAssignment( 429 factory.converters.convertToAssignmentElementTarget(node.name), 430 node.initializer! 431 ), 432 node 433 ); 434 return visitNode(converted, visitor, isExpression); 435 } 436 437 function collidesWithParameterName({ name }: VariableDeclaration | BindingElement): boolean { 438 if (isIdentifier(name)) { 439 return enclosingFunctionParameterNames.has(name.escapedText); 440 } 441 else { 442 for (const element of name.elements) { 443 if (!isOmittedExpression(element) && collidesWithParameterName(element)) { 444 return true; 445 } 446 } 447 } 448 return false; 449 } 450 451 function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; 452 function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody; 453 function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody { 454 resumeLexicalEnvironment(); 455 456 const original = getOriginalNode(node, isFunctionLike); 457 const nodeType = original.type; 458 const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; 459 const isArrowFunction = node.kind === SyntaxKind.ArrowFunction; 460 const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; 461 462 // An async function is emit as an outer function that calls an inner 463 // generator function. To preserve lexical bindings, we pass the current 464 // `this` and `arguments` objects to `__awaiter`. The generator function 465 // passed to `__awaiter` is executed inside of the callback to the 466 // promise constructor. 467 468 const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames; 469 enclosingFunctionParameterNames = new Set(); 470 for (const parameter of node.parameters) { 471 recordDeclarationName(parameter, enclosingFunctionParameterNames); 472 } 473 474 const savedCapturedSuperProperties = capturedSuperProperties; 475 const savedHasSuperElementAccess = hasSuperElementAccess; 476 if (!isArrowFunction) { 477 capturedSuperProperties = new Set(); 478 hasSuperElementAccess = false; 479 } 480 481 let result: ConciseBody; 482 if (!isArrowFunction) { 483 const statements: Statement[] = []; 484 const statementOffset = factory.copyPrologue((<Block>node.body).statements, statements, /*ensureUseStrict*/ false, visitor); 485 statements.push( 486 factory.createReturnStatement( 487 emitHelpers().createAwaiterHelper( 488 inHasLexicalThisContext(), 489 hasLexicalArguments, 490 promiseConstructor, 491 transformAsyncFunctionBodyWorker(<Block>node.body, statementOffset) 492 ) 493 ) 494 ); 495 496 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 497 498 // Minor optimization, emit `_super` helper to capture `super` access in an arrow. 499 // This step isn't needed if we eventually transform this to ES5. 500 const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuperBinding | NodeCheckFlags.AsyncMethodWithSuper); 501 502 if (emitSuperHelpers) { 503 enableSubstitutionForAsyncMethodsWithSuper(); 504 if (capturedSuperProperties.size) { 505 const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); 506 substitutedSuperAccessors[getNodeId(variableStatement)] = true; 507 insertStatementsAfterStandardPrologue(statements, [variableStatement]); 508 } 509 } 510 511 const block = factory.createBlock(statements, /*multiLine*/ true); 512 setTextRange(block, node.body); 513 514 if (emitSuperHelpers && hasSuperElementAccess) { 515 // Emit helpers for super element access expressions (`super[x]`). 516 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) { 517 addEmitHelper(block, advancedAsyncSuperHelper); 518 } 519 else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) { 520 addEmitHelper(block, asyncSuperHelper); 521 } 522 } 523 524 result = block; 525 } 526 else { 527 const expression = emitHelpers().createAwaiterHelper( 528 inHasLexicalThisContext(), 529 hasLexicalArguments, 530 promiseConstructor, 531 transformAsyncFunctionBodyWorker(node.body!) 532 ); 533 534 const declarations = endLexicalEnvironment(); 535 if (some(declarations)) { 536 const block = factory.converters.convertToFunctionBlock(expression); 537 result = factory.updateBlock(block, setTextRange(factory.createNodeArray(concatenate(declarations, block.statements)), block.statements)); 538 } 539 else { 540 result = expression; 541 } 542 } 543 544 enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames; 545 if (!isArrowFunction) { 546 capturedSuperProperties = savedCapturedSuperProperties; 547 hasSuperElementAccess = savedHasSuperElementAccess; 548 } 549 return result; 550 } 551 552 function transformAsyncFunctionBodyWorker(body: ConciseBody, start?: number) { 553 if (isBlock(body)) { 554 return factory.updateBlock(body, visitNodes(body.statements, asyncBodyVisitor, isStatement, start)); 555 } 556 else { 557 return factory.converters.convertToFunctionBlock(visitNode(body, asyncBodyVisitor, isConciseBody)); 558 } 559 } 560 561 function getPromiseConstructor(type: TypeNode | undefined) { 562 const typeName = type && getEntityNameFromTypeNode(type); 563 if (typeName && isEntityName(typeName)) { 564 const serializationKind = resolver.getTypeReferenceSerializationKind(typeName); 565 if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue 566 || serializationKind === TypeReferenceSerializationKind.Unknown) { 567 return typeName; 568 } 569 } 570 571 return undefined; 572 } 573 574 function enableSubstitutionForAsyncMethodsWithSuper() { 575 if ((enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper) === 0) { 576 enabledSubstitutions |= ES2017SubstitutionFlags.AsyncMethodsWithSuper; 577 578 // We need to enable substitutions for call, property access, and element access 579 // if we need to rewrite super calls. 580 context.enableSubstitution(SyntaxKind.CallExpression); 581 context.enableSubstitution(SyntaxKind.PropertyAccessExpression); 582 context.enableSubstitution(SyntaxKind.ElementAccessExpression); 583 584 // We need to be notified when entering and exiting declarations that bind super. 585 context.enableEmitNotification(SyntaxKind.ClassDeclaration); 586 context.enableEmitNotification(SyntaxKind.MethodDeclaration); 587 context.enableEmitNotification(SyntaxKind.GetAccessor); 588 context.enableEmitNotification(SyntaxKind.SetAccessor); 589 context.enableEmitNotification(SyntaxKind.Constructor); 590 // We need to be notified when entering the generated accessor arrow functions. 591 context.enableEmitNotification(SyntaxKind.VariableStatement); 592 } 593 } 594 595 /** 596 * Hook for node emit. 597 * 598 * @param hint A hint as to the intended usage of the node. 599 * @param node The node to emit. 600 * @param emit A callback used to emit the node in the printer. 601 */ 602 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 603 // If we need to support substitutions for `super` in an async method, 604 // we should track it here. 605 if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { 606 const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding); 607 if (superContainerFlags !== enclosingSuperContainerFlags) { 608 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 609 enclosingSuperContainerFlags = superContainerFlags; 610 previousOnEmitNode(hint, node, emitCallback); 611 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 612 return; 613 } 614 } 615 // Disable substitution in the generated super accessor itself. 616 else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { 617 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 618 enclosingSuperContainerFlags = 0; 619 previousOnEmitNode(hint, node, emitCallback); 620 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 621 return; 622 } 623 previousOnEmitNode(hint, node, emitCallback); 624 } 625 626 /** 627 * Hooks node substitutions. 628 * 629 * @param hint A hint as to the intended usage of the node. 630 * @param node The node to substitute. 631 */ 632 function onSubstituteNode(hint: EmitHint, node: Node) { 633 node = previousOnSubstituteNode(hint, node); 634 if (hint === EmitHint.Expression && enclosingSuperContainerFlags) { 635 return substituteExpression(<Expression>node); 636 } 637 638 return node; 639 } 640 641 function substituteExpression(node: Expression) { 642 switch (node.kind) { 643 case SyntaxKind.PropertyAccessExpression: 644 return substitutePropertyAccessExpression(<PropertyAccessExpression>node); 645 case SyntaxKind.ElementAccessExpression: 646 return substituteElementAccessExpression(<ElementAccessExpression>node); 647 case SyntaxKind.CallExpression: 648 return substituteCallExpression(<CallExpression>node); 649 } 650 return node; 651 } 652 653 function substitutePropertyAccessExpression(node: PropertyAccessExpression) { 654 if (node.expression.kind === SyntaxKind.SuperKeyword) { 655 return setTextRange( 656 factory.createPropertyAccessExpression( 657 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 658 node.name), 659 node 660 ); 661 } 662 return node; 663 } 664 665 function substituteElementAccessExpression(node: ElementAccessExpression) { 666 if (node.expression.kind === SyntaxKind.SuperKeyword) { 667 return createSuperElementAccessInAsyncMethod( 668 node.argumentExpression, 669 node 670 ); 671 } 672 return node; 673 } 674 675 function substituteCallExpression(node: CallExpression): Expression { 676 const expression = node.expression; 677 if (isSuperProperty(expression)) { 678 const argumentExpression = isPropertyAccessExpression(expression) 679 ? substitutePropertyAccessExpression(expression) 680 : substituteElementAccessExpression(expression); 681 return factory.createCallExpression( 682 factory.createPropertyAccessExpression(argumentExpression, "call"), 683 /*typeArguments*/ undefined, 684 [ 685 factory.createThis(), 686 ...node.arguments 687 ] 688 ); 689 } 690 return node; 691 } 692 693 function isSuperContainer(node: Node): node is SuperContainer { 694 const kind = node.kind; 695 return kind === SyntaxKind.ClassDeclaration 696 || kind === SyntaxKind.Constructor 697 || kind === SyntaxKind.MethodDeclaration 698 || kind === SyntaxKind.GetAccessor 699 || kind === SyntaxKind.SetAccessor; 700 } 701 702 function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression { 703 if (enclosingSuperContainerFlags & NodeCheckFlags.AsyncMethodWithSuperBinding) { 704 return setTextRange( 705 factory.createPropertyAccessExpression( 706 factory.createCallExpression( 707 factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 708 /*typeArguments*/ undefined, 709 [argumentExpression] 710 ), 711 "value" 712 ), 713 location 714 ); 715 } 716 else { 717 return setTextRange( 718 factory.createCallExpression( 719 factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 720 /*typeArguments*/ undefined, 721 [argumentExpression] 722 ), 723 location 724 ); 725 } 726 } 727 } 728 729 /** Creates a variable named `_super` with accessor properties for the given property names. */ 730 export function createSuperAccessVariableStatement(factory: NodeFactory, resolver: EmitResolver, node: FunctionLikeDeclaration, names: Set<__String>) { 731 // Create a variable declaration with a getter/setter (if binding) definition for each name: 732 // const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... }); 733 const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) !== 0; 734 const accessors: PropertyAssignment[] = []; 735 names.forEach((_, key) => { 736 const name = unescapeLeadingUnderscores(key); 737 const getterAndSetter: PropertyAssignment[] = []; 738 getterAndSetter.push(factory.createPropertyAssignment( 739 "get", 740 factory.createArrowFunction( 741 /* modifiers */ undefined, 742 /* typeParameters */ undefined, 743 /* parameters */ [], 744 /* type */ undefined, 745 /* equalsGreaterThanToken */ undefined, 746 setEmitFlags( 747 factory.createPropertyAccessExpression( 748 setEmitFlags( 749 factory.createSuper(), 750 EmitFlags.NoSubstitution 751 ), 752 name 753 ), 754 EmitFlags.NoSubstitution 755 ) 756 ) 757 )); 758 if (hasBinding) { 759 getterAndSetter.push( 760 factory.createPropertyAssignment( 761 "set", 762 factory.createArrowFunction( 763 /* modifiers */ undefined, 764 /* typeParameters */ undefined, 765 /* parameters */ [ 766 factory.createParameterDeclaration( 767 /* decorators */ undefined, 768 /* modifiers */ undefined, 769 /* dotDotDotToken */ undefined, 770 "v", 771 /* questionToken */ undefined, 772 /* type */ undefined, 773 /* initializer */ undefined 774 ) 775 ], 776 /* type */ undefined, 777 /* equalsGreaterThanToken */ undefined, 778 factory.createAssignment( 779 setEmitFlags( 780 factory.createPropertyAccessExpression( 781 setEmitFlags( 782 factory.createSuper(), 783 EmitFlags.NoSubstitution 784 ), 785 name 786 ), 787 EmitFlags.NoSubstitution 788 ), 789 factory.createIdentifier("v") 790 ) 791 ) 792 ) 793 ); 794 } 795 accessors.push( 796 factory.createPropertyAssignment( 797 name, 798 factory.createObjectLiteralExpression(getterAndSetter), 799 ) 800 ); 801 }); 802 return factory.createVariableStatement( 803 /* modifiers */ undefined, 804 factory.createVariableDeclarationList( 805 [ 806 factory.createVariableDeclaration( 807 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 808 /*exclamationToken*/ undefined, 809 /* type */ undefined, 810 factory.createCallExpression( 811 factory.createPropertyAccessExpression( 812 factory.createIdentifier("Object"), 813 "create" 814 ), 815 /* typeArguments */ undefined, 816 [ 817 factory.createNull(), 818 factory.createObjectLiteralExpression(accessors, /* multiline */ true) 819 ] 820 ) 821 ) 822 ], 823 NodeFlags.Const)); 824 } 825} 826