1/*@internal*/ 2namespace ts { 3 const enum ESNextSubstitutionFlags { 4 /** Enables substitutions for async methods with `super` calls. */ 5 AsyncMethodsWithSuper = 1 << 0 6 } 7 8 // Facts we track as we traverse the tree 9 const enum HierarchyFacts { 10 None = 0, 11 12 // 13 // Ancestor facts 14 // 15 16 HasLexicalThis = 1 << 0, 17 IterationContainer = 1 << 1, 18 // NOTE: do not add more ancestor flags without also updating AncestorFactsMask below. 19 20 // 21 // Ancestor masks 22 // 23 24 AncestorFactsMask = (IterationContainer << 1) - 1, 25 26 SourceFileIncludes = HasLexicalThis, 27 SourceFileExcludes = IterationContainer, 28 StrictModeSourceFileIncludes = None, 29 30 ClassOrFunctionIncludes = HasLexicalThis, 31 ClassOrFunctionExcludes = IterationContainer, 32 33 ArrowFunctionIncludes = None, 34 ArrowFunctionExcludes = ClassOrFunctionExcludes, 35 36 IterationStatementIncludes = IterationContainer, 37 IterationStatementExcludes = None, 38 } 39 40 export function transformES2018(context: TransformationContext) { 41 const { 42 factory, 43 getEmitHelperFactory: emitHelpers, 44 resumeLexicalEnvironment, 45 endLexicalEnvironment, 46 hoistVariableDeclaration 47 } = context; 48 49 const resolver = context.getEmitResolver(); 50 const compilerOptions = context.getCompilerOptions(); 51 const languageVersion = getEmitScriptTarget(compilerOptions); 52 53 const previousOnEmitNode = context.onEmitNode; 54 context.onEmitNode = onEmitNode; 55 56 const previousOnSubstituteNode = context.onSubstituteNode; 57 context.onSubstituteNode = onSubstituteNode; 58 59 let exportedVariableStatement = false; 60 let enabledSubstitutions: ESNextSubstitutionFlags; 61 let enclosingFunctionFlags: FunctionFlags; 62 let parametersWithPrecedingObjectRestOrSpread: Set<ParameterDeclaration> | undefined; 63 let enclosingSuperContainerFlags: NodeCheckFlags = 0; 64 let hierarchyFacts: HierarchyFacts = 0; 65 66 let currentSourceFile: SourceFile; 67 let taggedTemplateStringDeclarations: VariableDeclaration[]; 68 69 /** Keeps track of property names accessed on super (`super.x`) within async functions. */ 70 let capturedSuperProperties: Set<__String>; 71 /** Whether the async function contains an element access on super (`super[x]`). */ 72 let hasSuperElementAccess: boolean; 73 /** A set of node IDs for generated super accessors. */ 74 const substitutedSuperAccessors: boolean[] = []; 75 76 return chainBundle(context, transformSourceFile); 77 78 function affectsSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) { 79 return hierarchyFacts !== (hierarchyFacts & ~excludeFacts | includeFacts); 80 } 81 82 /** 83 * Sets the `HierarchyFacts` for this node prior to visiting this node's subtree, returning the facts set prior to modification. 84 * @param excludeFacts The existing `HierarchyFacts` to reset before visiting the subtree. 85 * @param includeFacts The new `HierarchyFacts` to set before visiting the subtree. 86 */ 87 function enterSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) { 88 const ancestorFacts = hierarchyFacts; 89 hierarchyFacts = (hierarchyFacts & ~excludeFacts | includeFacts) & HierarchyFacts.AncestorFactsMask; 90 return ancestorFacts; 91 } 92 93 /** 94 * Restores the `HierarchyFacts` for this node's ancestor after visiting this node's 95 * subtree. 96 * @param ancestorFacts The `HierarchyFacts` of the ancestor to restore after visiting the subtree. 97 */ 98 function exitSubtree(ancestorFacts: HierarchyFacts) { 99 hierarchyFacts = ancestorFacts; 100 } 101 102 function recordTaggedTemplateString(temp: Identifier) { 103 taggedTemplateStringDeclarations = append( 104 taggedTemplateStringDeclarations, 105 factory.createVariableDeclaration(temp)); 106 } 107 108 function transformSourceFile(node: SourceFile) { 109 if (node.isDeclarationFile) { 110 return node; 111 } 112 113 currentSourceFile = node; 114 const visited = visitSourceFile(node); 115 addEmitHelpers(visited, context.readEmitHelpers()); 116 117 currentSourceFile = undefined!; 118 taggedTemplateStringDeclarations = undefined!; 119 return visited; 120 } 121 122 function visitor(node: Node): VisitResult<Node> { 123 return visitorWorker(node, /*expressionResultIsUnused*/ false); 124 } 125 126 function visitorWithUnusedExpressionResult(node: Node): VisitResult<Node> { 127 return visitorWorker(node, /*expressionResultIsUnused*/ true); 128 } 129 130 function visitorNoAsyncModifier(node: Node): VisitResult<Node> { 131 if (node.kind === SyntaxKind.AsyncKeyword) { 132 return undefined; 133 } 134 return node; 135 } 136 137 function doWithHierarchyFacts<T, U>(cb: (value: T) => U, value: T, excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) { 138 if (affectsSubtree(excludeFacts, includeFacts)) { 139 const ancestorFacts = enterSubtree(excludeFacts, includeFacts); 140 const result = cb(value); 141 exitSubtree(ancestorFacts); 142 return result; 143 } 144 return cb(value); 145 } 146 147 function visitDefault(node: Node): VisitResult<Node> { 148 return visitEachChild(node, visitor, context); 149 } 150 151 /** 152 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 153 * expression of an `ExpressionStatement`). 154 */ 155 function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult<Node> { 156 if ((node.transformFlags & TransformFlags.ContainsES2018) === 0) { 157 return node; 158 } 159 switch (node.kind) { 160 case SyntaxKind.AwaitExpression: 161 return visitAwaitExpression(node as AwaitExpression); 162 case SyntaxKind.YieldExpression: 163 return visitYieldExpression(node as YieldExpression); 164 case SyntaxKind.ReturnStatement: 165 return visitReturnStatement(node as ReturnStatement); 166 case SyntaxKind.LabeledStatement: 167 return visitLabeledStatement(node as LabeledStatement); 168 case SyntaxKind.ObjectLiteralExpression: 169 return visitObjectLiteralExpression(node as ObjectLiteralExpression); 170 case SyntaxKind.BinaryExpression: 171 return visitBinaryExpression(node as BinaryExpression, expressionResultIsUnused); 172 case SyntaxKind.CommaListExpression: 173 return visitCommaListExpression(node as CommaListExpression, expressionResultIsUnused); 174 case SyntaxKind.CatchClause: 175 return visitCatchClause(node as CatchClause); 176 case SyntaxKind.VariableStatement: 177 return visitVariableStatement(node as VariableStatement); 178 case SyntaxKind.VariableDeclaration: 179 return visitVariableDeclaration(node as VariableDeclaration); 180 case SyntaxKind.DoStatement: 181 case SyntaxKind.WhileStatement: 182 case SyntaxKind.ForInStatement: 183 return doWithHierarchyFacts( 184 visitDefault, 185 node, 186 HierarchyFacts.IterationStatementExcludes, 187 HierarchyFacts.IterationStatementIncludes); 188 case SyntaxKind.ForOfStatement: 189 return visitForOfStatement(node as ForOfStatement, /*outermostLabeledStatement*/ undefined); 190 case SyntaxKind.ForStatement: 191 return doWithHierarchyFacts( 192 visitForStatement, 193 node as ForStatement, 194 HierarchyFacts.IterationStatementExcludes, 195 HierarchyFacts.IterationStatementIncludes); 196 case SyntaxKind.VoidExpression: 197 return visitVoidExpression(node as VoidExpression); 198 case SyntaxKind.Constructor: 199 return doWithHierarchyFacts( 200 visitConstructorDeclaration, 201 node as ConstructorDeclaration, 202 HierarchyFacts.ClassOrFunctionExcludes, 203 HierarchyFacts.ClassOrFunctionIncludes); 204 case SyntaxKind.MethodDeclaration: 205 return doWithHierarchyFacts( 206 visitMethodDeclaration, 207 node as MethodDeclaration, 208 HierarchyFacts.ClassOrFunctionExcludes, 209 HierarchyFacts.ClassOrFunctionIncludes); 210 case SyntaxKind.GetAccessor: 211 return doWithHierarchyFacts( 212 visitGetAccessorDeclaration, 213 node as GetAccessorDeclaration, 214 HierarchyFacts.ClassOrFunctionExcludes, 215 HierarchyFacts.ClassOrFunctionIncludes); 216 case SyntaxKind.SetAccessor: 217 return doWithHierarchyFacts( 218 visitSetAccessorDeclaration, 219 node as SetAccessorDeclaration, 220 HierarchyFacts.ClassOrFunctionExcludes, 221 HierarchyFacts.ClassOrFunctionIncludes); 222 case SyntaxKind.FunctionDeclaration: 223 return doWithHierarchyFacts( 224 visitFunctionDeclaration, 225 node as FunctionDeclaration, 226 HierarchyFacts.ClassOrFunctionExcludes, 227 HierarchyFacts.ClassOrFunctionIncludes); 228 case SyntaxKind.FunctionExpression: 229 return doWithHierarchyFacts( 230 visitFunctionExpression, 231 node as FunctionExpression, 232 HierarchyFacts.ClassOrFunctionExcludes, 233 HierarchyFacts.ClassOrFunctionIncludes); 234 case SyntaxKind.ArrowFunction: 235 return doWithHierarchyFacts( 236 visitArrowFunction, 237 node as ArrowFunction, 238 HierarchyFacts.ArrowFunctionExcludes, 239 HierarchyFacts.ArrowFunctionIncludes); 240 case SyntaxKind.Parameter: 241 return visitParameter(node as ParameterDeclaration); 242 case SyntaxKind.ExpressionStatement: 243 return visitExpressionStatement(node as ExpressionStatement); 244 case SyntaxKind.ParenthesizedExpression: 245 return visitParenthesizedExpression(node as ParenthesizedExpression, expressionResultIsUnused); 246 case SyntaxKind.TaggedTemplateExpression: 247 return visitTaggedTemplateExpression(node as TaggedTemplateExpression); 248 case SyntaxKind.PropertyAccessExpression: 249 if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) { 250 capturedSuperProperties.add(node.name.escapedText); 251 } 252 return visitEachChild(node, visitor, context); 253 case SyntaxKind.ElementAccessExpression: 254 if (capturedSuperProperties && (node as ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword) { 255 hasSuperElementAccess = true; 256 } 257 return visitEachChild(node, visitor, context); 258 case SyntaxKind.ClassDeclaration: 259 case SyntaxKind.ClassExpression: 260 return doWithHierarchyFacts( 261 visitDefault, 262 node, 263 HierarchyFacts.ClassOrFunctionExcludes, 264 HierarchyFacts.ClassOrFunctionIncludes); 265 default: 266 return visitEachChild(node, visitor, context); 267 } 268 } 269 270 function visitAwaitExpression(node: AwaitExpression): Expression { 271 if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { 272 return setOriginalNode( 273 setTextRange( 274 factory.createYieldExpression(/*asteriskToken*/ undefined, emitHelpers().createAwaitHelper(visitNode(node.expression, visitor, isExpression))), 275 /*location*/ node 276 ), 277 node 278 ); 279 } 280 return visitEachChild(node, visitor, context); 281 } 282 283 function visitYieldExpression(node: YieldExpression) { 284 if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { 285 if (node.asteriskToken) { 286 const expression = visitNode(Debug.checkDefined(node.expression), visitor, isExpression); 287 288 return setOriginalNode( 289 setTextRange( 290 factory.createYieldExpression( 291 /*asteriskToken*/ undefined, 292 emitHelpers().createAwaitHelper( 293 factory.updateYieldExpression( 294 node, 295 node.asteriskToken, 296 setTextRange( 297 emitHelpers().createAsyncDelegatorHelper( 298 setTextRange( 299 emitHelpers().createAsyncValuesHelper(expression), 300 expression 301 ) 302 ), 303 expression 304 ) 305 ) 306 ) 307 ), 308 node 309 ), 310 node 311 ); 312 } 313 314 return setOriginalNode( 315 setTextRange( 316 factory.createYieldExpression( 317 /*asteriskToken*/ undefined, 318 createDownlevelAwait( 319 node.expression 320 ? visitNode(node.expression, visitor, isExpression) 321 : factory.createVoidZero() 322 ) 323 ), 324 node 325 ), 326 node 327 ); 328 } 329 330 return visitEachChild(node, visitor, context); 331 } 332 333 function visitReturnStatement(node: ReturnStatement) { 334 if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { 335 return factory.updateReturnStatement(node, createDownlevelAwait( 336 node.expression ? visitNode(node.expression, visitor, isExpression) : factory.createVoidZero() 337 )); 338 } 339 340 return visitEachChild(node, visitor, context); 341 } 342 343 function visitLabeledStatement(node: LabeledStatement) { 344 if (enclosingFunctionFlags & FunctionFlags.Async) { 345 const statement = unwrapInnermostStatementOfLabel(node); 346 if (statement.kind === SyntaxKind.ForOfStatement && (statement as ForOfStatement).awaitModifier) { 347 return visitForOfStatement(statement as ForOfStatement, node); 348 } 349 return factory.restoreEnclosingLabel(visitNode(statement, visitor, isStatement, factory.liftToBlock), node); 350 } 351 return visitEachChild(node, visitor, context); 352 } 353 354 function chunkObjectLiteralElements(elements: readonly ObjectLiteralElementLike[]): Expression[] { 355 let chunkObject: ObjectLiteralElementLike[] | undefined; 356 const objects: Expression[] = []; 357 for (const e of elements) { 358 if (e.kind === SyntaxKind.SpreadAssignment) { 359 if (chunkObject) { 360 objects.push(factory.createObjectLiteralExpression(chunkObject)); 361 chunkObject = undefined; 362 } 363 const target = e.expression; 364 objects.push(visitNode(target, visitor, isExpression)); 365 } 366 else { 367 chunkObject = append(chunkObject, e.kind === SyntaxKind.PropertyAssignment 368 ? factory.createPropertyAssignment(e.name, visitNode(e.initializer, visitor, isExpression)) 369 : visitNode(e, visitor, isObjectLiteralElementLike)); 370 } 371 } 372 if (chunkObject) { 373 objects.push(factory.createObjectLiteralExpression(chunkObject)); 374 } 375 376 return objects; 377 } 378 379 function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { 380 if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 381 // spread elements emit like so: 382 // non-spread elements are chunked together into object literals, and then all are passed to __assign: 383 // { a, ...o, b } => __assign(__assign({a}, o), {b}); 384 // If the first element is a spread element, then the first argument to __assign is {}: 385 // { ...o, a, b, ...o2 } => __assign(__assign(__assign({}, o), {a, b}), o2) 386 // 387 // We cannot call __assign with more than two elements, since any element could cause side effects. For 388 // example: 389 // var k = { a: 1, b: 2 }; 390 // var o = { a: 3, ...k, b: k.a++ }; 391 // // expected: { a: 1, b: 1 } 392 // If we translate the above to `__assign({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before 393 // `k` is spread and we end up with `{ a: 2, b: 1 }`. 394 // 395 // This also occurs for spread elements, not just property assignments: 396 // var k = { a: 1, get b() { l = { z: 9 }; return 2; } }; 397 // var l = { c: 3 }; 398 // var o = { ...k, ...l }; 399 // // expected: { a: 1, b: 2, z: 9 } 400 // If we translate the above to `__assign({}, k, l)`, the `l` will evaluate before `k` is spread and we 401 // end up with `{ a: 1, b: 2, c: 3 }` 402 const objects = chunkObjectLiteralElements(node.properties); 403 if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) { 404 objects.unshift(factory.createObjectLiteralExpression()); 405 } 406 let expression: Expression = objects[0]; 407 if (objects.length > 1) { 408 for (let i = 1; i < objects.length; i++) { 409 expression = emitHelpers().createAssignHelper([expression, objects[i]]); 410 } 411 return expression; 412 } 413 else { 414 return emitHelpers().createAssignHelper(objects); 415 } 416 } 417 return visitEachChild(node, visitor, context); 418 } 419 420 function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { 421 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 422 } 423 424 /** 425 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 426 * expression of an `ExpressionStatement`). 427 */ 428 function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression { 429 return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context); 430 } 431 432 function visitSourceFile(node: SourceFile): SourceFile { 433 const ancestorFacts = enterSubtree( 434 HierarchyFacts.SourceFileExcludes, 435 isEffectiveStrictModeSourceFile(node, compilerOptions) ? 436 HierarchyFacts.StrictModeSourceFileIncludes : 437 HierarchyFacts.SourceFileIncludes); 438 exportedVariableStatement = false; 439 const visited = visitEachChild(node, visitor, context); 440 const statement = concatenate(visited.statements, taggedTemplateStringDeclarations && [ 441 factory.createVariableStatement(/*modifiers*/ undefined, 442 factory.createVariableDeclarationList(taggedTemplateStringDeclarations)) 443 ]); 444 const result = factory.updateSourceFile(visited, setTextRange(factory.createNodeArray(statement), node.statements)); 445 exitSubtree(ancestorFacts); 446 return result; 447 } 448 449 function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { 450 return processTaggedTemplateExpression( 451 context, 452 node, 453 visitor, 454 currentSourceFile, 455 recordTaggedTemplateString, 456 ProcessLevel.LiftRestriction 457 ); 458 } 459 460 /** 461 * Visits a BinaryExpression that contains a destructuring assignment. 462 * 463 * @param node A BinaryExpression node. 464 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 465 * expression of an `ExpressionStatement`). 466 */ 467 function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression { 468 if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 469 return flattenDestructuringAssignment( 470 node, 471 visitor, 472 context, 473 FlattenLevel.ObjectRest, 474 !expressionResultIsUnused 475 ); 476 } 477 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 478 return factory.updateBinaryExpression( 479 node, 480 visitNode(node.left, visitorWithUnusedExpressionResult, isExpression), 481 node.operatorToken, 482 visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression) 483 ); 484 } 485 return visitEachChild(node, visitor, context); 486 } 487 488 /** 489 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 490 * expression of an `ExpressionStatement`). 491 */ 492 function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression { 493 if (expressionResultIsUnused) { 494 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 495 } 496 let result: Expression[] | undefined; 497 for (let i = 0; i < node.elements.length; i++) { 498 const element = node.elements[i]; 499 const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression); 500 if (result || visited !== element) { 501 result ||= node.elements.slice(0, i); 502 result.push(visited); 503 } 504 } 505 const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements; 506 return factory.updateCommaListExpression(node, elements); 507 } 508 509 function visitCatchClause(node: CatchClause) { 510 if (node.variableDeclaration && 511 isBindingPattern(node.variableDeclaration.name) && 512 node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 513 const name = factory.getGeneratedNameForNode(node.variableDeclaration.name); 514 const updatedDecl = factory.updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*exclamationToken*/ undefined, /*type*/ undefined, name); 515 const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest); 516 let block = visitNode(node.block, visitor, isBlock); 517 if (some(visitedBindings)) { 518 block = factory.updateBlock(block, [ 519 factory.createVariableStatement(/*modifiers*/ undefined, visitedBindings), 520 ...block.statements, 521 ]); 522 } 523 return factory.updateCatchClause( 524 node, 525 factory.updateVariableDeclaration(node.variableDeclaration, name, /*exclamationToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined), 526 block); 527 } 528 return visitEachChild(node, visitor, context); 529 } 530 531 function visitVariableStatement(node: VariableStatement): VisitResult<VariableStatement> { 532 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 533 const savedExportedVariableStatement = exportedVariableStatement; 534 exportedVariableStatement = true; 535 const visited = visitEachChild(node, visitor, context); 536 exportedVariableStatement = savedExportedVariableStatement; 537 return visited; 538 } 539 return visitEachChild(node, visitor, context); 540 } 541 542 /** 543 * Visits a VariableDeclaration node with a binding pattern. 544 * 545 * @param node A VariableDeclaration node. 546 */ 547 function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> { 548 if (exportedVariableStatement) { 549 const savedExportedVariableStatement = exportedVariableStatement; 550 exportedVariableStatement = false; 551 const visited = visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ true); 552 exportedVariableStatement = savedExportedVariableStatement; 553 return visited; 554 } 555 return visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ false); 556 } 557 558 function visitVariableDeclarationWorker(node: VariableDeclaration, exportedVariableStatement: boolean): VisitResult<VariableDeclaration> { 559 // If we are here it is because the name contains a binding pattern with a rest somewhere in it. 560 if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 561 return flattenDestructuringBinding( 562 node, 563 visitor, 564 context, 565 FlattenLevel.ObjectRest, 566 /*rval*/ undefined, 567 exportedVariableStatement 568 ); 569 } 570 return visitEachChild(node, visitor, context); 571 } 572 573 function visitForStatement(node: ForStatement): VisitResult<Statement> { 574 return factory.updateForStatement( 575 node, 576 visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer), 577 visitNode(node.condition, visitor, isExpression), 578 visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression), 579 visitIterationBody(node.statement, visitor, context) 580 ); 581 } 582 583 function visitVoidExpression(node: VoidExpression) { 584 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 585 } 586 587 /** 588 * Visits a ForOfStatement and converts it into a ES2015-compatible ForOfStatement. 589 * 590 * @param node A ForOfStatement. 591 */ 592 function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> { 593 const ancestorFacts = enterSubtree(HierarchyFacts.IterationStatementExcludes, HierarchyFacts.IterationStatementIncludes); 594 if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 595 node = transformForOfStatementWithObjectRest(node); 596 } 597 const result = node.awaitModifier ? 598 transformForAwaitOfStatement(node, outermostLabeledStatement, ancestorFacts) : 599 factory.restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement); 600 exitSubtree(ancestorFacts); 601 return result; 602 } 603 604 function transformForOfStatementWithObjectRest(node: ForOfStatement) { 605 const initializerWithoutParens = skipParentheses(node.initializer) as ForInitializer; 606 if (isVariableDeclarationList(initializerWithoutParens) || isAssignmentPattern(initializerWithoutParens)) { 607 let bodyLocation: TextRange | undefined; 608 let statementsLocation: TextRange | undefined; 609 const temp = factory.createTempVariable(/*recordTempVariable*/ undefined); 610 const statements: Statement[] = [createForOfBindingStatement(factory, initializerWithoutParens, temp)]; 611 if (isBlock(node.statement)) { 612 addRange(statements, node.statement.statements); 613 bodyLocation = node.statement; 614 statementsLocation = node.statement.statements; 615 } 616 else if (node.statement) { 617 append(statements, node.statement); 618 bodyLocation = node.statement; 619 statementsLocation = node.statement; 620 } 621 return factory.updateForOfStatement( 622 node, 623 node.awaitModifier, 624 setTextRange( 625 factory.createVariableDeclarationList( 626 [ 627 setTextRange(factory.createVariableDeclaration(temp), node.initializer) 628 ], 629 NodeFlags.Let 630 ), 631 node.initializer 632 ), 633 node.expression, 634 setTextRange( 635 factory.createBlock( 636 setTextRange(factory.createNodeArray(statements), statementsLocation), 637 /*multiLine*/ true 638 ), 639 bodyLocation 640 ) 641 ); 642 } 643 return node; 644 } 645 646 function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression, nonUserCode: Identifier) { 647 const value = factory.createTempVariable(hoistVariableDeclaration); 648 const iteratorValueExpression = factory.createAssignment(value, boundValue); 649 const iteratorValueStatement = factory.createExpressionStatement(iteratorValueExpression); 650 setSourceMapRange(iteratorValueStatement, node.expression); 651 652 const exitNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createFalse()); 653 const exitNonUserCodeStatement = factory.createExpressionStatement(exitNonUserCodeExpression); 654 setSourceMapRange(exitNonUserCodeStatement, node.expression); 655 656 const enterNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createTrue()); 657 const enterNonUserCodeStatement = factory.createExpressionStatement(enterNonUserCodeExpression); 658 setSourceMapRange(exitNonUserCodeStatement, node.expression); 659 660 const statements: Statement[] = []; 661 const binding = createForOfBindingStatement(factory, node.initializer, value); 662 statements.push(visitNode(binding, visitor, isStatement)); 663 664 let bodyLocation: TextRange | undefined; 665 let statementsLocation: TextRange | undefined; 666 const statement = visitIterationBody(node.statement, visitor, context); 667 if (isBlock(statement)) { 668 addRange(statements, statement.statements); 669 bodyLocation = statement; 670 statementsLocation = statement.statements; 671 } 672 else { 673 statements.push(statement); 674 } 675 676 const body = setEmitFlags( 677 setTextRange( 678 factory.createBlock( 679 setTextRange(factory.createNodeArray(statements), statementsLocation), 680 /*multiLine*/ true 681 ), 682 bodyLocation 683 ), 684 EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps 685 ); 686 687 return factory.createBlock([ 688 iteratorValueStatement, 689 exitNonUserCodeStatement, 690 factory.createTryStatement( 691 body, 692 /*catchClause*/ undefined, 693 factory.createBlock([ 694 enterNonUserCodeStatement 695 ]) 696 ) 697 ]); 698 } 699 700 function createDownlevelAwait(expression: Expression) { 701 return enclosingFunctionFlags & FunctionFlags.Generator 702 ? factory.createYieldExpression(/*asteriskToken*/ undefined, emitHelpers().createAwaitHelper(expression)) 703 : factory.createAwaitExpression(expression); 704 } 705 706 function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts) { 707 const expression = visitNode(node.expression, visitor, isExpression); 708 const iterator = isIdentifier(expression) ? factory.getGeneratedNameForNode(expression) : factory.createTempVariable(/*recordTempVariable*/ undefined); 709 const result = isIdentifier(expression) ? factory.getGeneratedNameForNode(iterator) : factory.createTempVariable(/*recordTempVariable*/ undefined); 710 const nonUserCode = factory.createTempVariable(/*recordTempVariable*/ undefined); 711 const done = factory.createTempVariable(hoistVariableDeclaration); 712 const errorRecord = factory.createUniqueName("e"); 713 const catchVariable = factory.getGeneratedNameForNode(errorRecord); 714 const returnMethod = factory.createTempVariable(/*recordTempVariable*/ undefined); 715 const callValues = setTextRange(emitHelpers().createAsyncValuesHelper(expression), node.expression); 716 const callNext = factory.createCallExpression(factory.createPropertyAccessExpression(iterator, "next"), /*typeArguments*/ undefined, []); 717 const getDone = factory.createPropertyAccessExpression(result, "done"); 718 const getValue = factory.createPropertyAccessExpression(result, "value"); 719 const callReturn = factory.createFunctionCallCall(returnMethod, iterator, []); 720 721 hoistVariableDeclaration(errorRecord); 722 hoistVariableDeclaration(returnMethod); 723 724 // if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration 725 const initializer = ancestorFacts & HierarchyFacts.IterationContainer ? 726 factory.inlineExpressions([factory.createAssignment(errorRecord, factory.createVoidZero()), callValues]) : 727 callValues; 728 729 const forStatement = setEmitFlags( 730 setTextRange( 731 factory.createForStatement( 732 /*initializer*/ setEmitFlags( 733 setTextRange( 734 factory.createVariableDeclarationList([ 735 factory.createVariableDeclaration(nonUserCode, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createTrue()), 736 setTextRange(factory.createVariableDeclaration(iterator, /*exclamationToken*/ undefined, /*type*/ undefined, initializer), node.expression), 737 factory.createVariableDeclaration(result) 738 ]), 739 node.expression 740 ), 741 EmitFlags.NoHoisting 742 ), 743 /*condition*/ factory.inlineExpressions([ 744 factory.createAssignment(result, createDownlevelAwait(callNext)), 745 factory.createAssignment(done, getDone), 746 factory.createLogicalNot(done) 747 ]), 748 /*incrementor*/ undefined, 749 /*statement*/ convertForOfStatementHead(node, getValue, nonUserCode) 750 ), 751 /*location*/ node 752 ), 753 EmitFlags.NoTokenTrailingSourceMaps 754 ); 755 setOriginalNode(forStatement, node); 756 757 return factory.createTryStatement( 758 factory.createBlock([ 759 factory.restoreEnclosingLabel( 760 forStatement, 761 outermostLabeledStatement 762 ) 763 ]), 764 factory.createCatchClause( 765 factory.createVariableDeclaration(catchVariable), 766 setEmitFlags( 767 factory.createBlock([ 768 factory.createExpressionStatement( 769 factory.createAssignment( 770 errorRecord, 771 factory.createObjectLiteralExpression([ 772 factory.createPropertyAssignment("error", catchVariable) 773 ]) 774 ) 775 ) 776 ]), 777 EmitFlags.SingleLine 778 ) 779 ), 780 factory.createBlock([ 781 factory.createTryStatement( 782 /*tryBlock*/ factory.createBlock([ 783 setEmitFlags( 784 factory.createIfStatement( 785 factory.createLogicalAnd( 786 factory.createLogicalAnd( 787 factory.createLogicalNot(nonUserCode), 788 factory.createLogicalNot(done), 789 ), 790 factory.createAssignment( 791 returnMethod, 792 factory.createPropertyAccessExpression(iterator, "return") 793 ) 794 ), 795 factory.createExpressionStatement(createDownlevelAwait(callReturn)) 796 ), 797 EmitFlags.SingleLine 798 ) 799 ]), 800 /*catchClause*/ undefined, 801 /*finallyBlock*/ setEmitFlags( 802 factory.createBlock([ 803 setEmitFlags( 804 factory.createIfStatement( 805 errorRecord, 806 factory.createThrowStatement( 807 factory.createPropertyAccessExpression(errorRecord, "error") 808 ) 809 ), 810 EmitFlags.SingleLine 811 ) 812 ]), 813 EmitFlags.SingleLine 814 ) 815 ) 816 ]) 817 ); 818 } 819 820 function parameterVisitor(node: Node) { 821 Debug.assertNode(node, isParameter); 822 return visitParameter(node); 823 } 824 825 function visitParameter(node: ParameterDeclaration): ParameterDeclaration { 826 if (parametersWithPrecedingObjectRestOrSpread?.has(node)) { 827 return factory.updateParameterDeclaration( 828 node, 829 /*modifiers*/ undefined, 830 node.dotDotDotToken, 831 isBindingPattern(node.name) ? factory.getGeneratedNameForNode(node) : node.name, 832 /*questionToken*/ undefined, 833 /*type*/ undefined, 834 /*initializer*/ undefined 835 ); 836 } 837 if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 838 // Binding patterns are converted into a generated name and are 839 // evaluated inside the function body. 840 return factory.updateParameterDeclaration( 841 node, 842 /*modifiers*/ undefined, 843 node.dotDotDotToken, 844 factory.getGeneratedNameForNode(node), 845 /*questionToken*/ undefined, 846 /*type*/ undefined, 847 visitNode(node.initializer, visitor, isExpression) 848 ); 849 } 850 return visitEachChild(node, visitor, context); 851 } 852 853 function collectParametersWithPrecedingObjectRestOrSpread(node: SignatureDeclaration) { 854 let parameters: Set<ParameterDeclaration> | undefined; 855 for (const parameter of node.parameters) { 856 if (parameters) { 857 parameters.add(parameter); 858 } 859 else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 860 parameters = new Set(); 861 } 862 } 863 return parameters; 864 } 865 866 function visitConstructorDeclaration(node: ConstructorDeclaration) { 867 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 868 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 869 enclosingFunctionFlags = getFunctionFlags(node); 870 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 871 const updated = factory.updateConstructorDeclaration( 872 node, 873 node.modifiers, 874 visitParameterList(node.parameters, parameterVisitor, context), 875 transformFunctionBody(node) 876 ); 877 enclosingFunctionFlags = savedEnclosingFunctionFlags; 878 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 879 return updated; 880 } 881 882 function visitGetAccessorDeclaration(node: GetAccessorDeclaration) { 883 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 884 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 885 enclosingFunctionFlags = getFunctionFlags(node); 886 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 887 const updated = factory.updateGetAccessorDeclaration( 888 node, 889 node.modifiers, 890 visitNode(node.name, visitor, isPropertyName), 891 visitParameterList(node.parameters, parameterVisitor, context), 892 /*type*/ undefined, 893 transformFunctionBody(node) 894 ); 895 enclosingFunctionFlags = savedEnclosingFunctionFlags; 896 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 897 return updated; 898 } 899 900 function visitSetAccessorDeclaration(node: SetAccessorDeclaration) { 901 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 902 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 903 enclosingFunctionFlags = getFunctionFlags(node); 904 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 905 const updated = factory.updateSetAccessorDeclaration( 906 node, 907 node.modifiers, 908 visitNode(node.name, visitor, isPropertyName), 909 visitParameterList(node.parameters, parameterVisitor, context), 910 transformFunctionBody(node) 911 ); 912 enclosingFunctionFlags = savedEnclosingFunctionFlags; 913 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 914 return updated; 915 } 916 917 function visitMethodDeclaration(node: MethodDeclaration) { 918 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 919 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 920 enclosingFunctionFlags = getFunctionFlags(node); 921 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 922 const updated = factory.updateMethodDeclaration( 923 node, 924 enclosingFunctionFlags & FunctionFlags.Generator 925 ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifierLike) 926 : node.modifiers, 927 enclosingFunctionFlags & FunctionFlags.Async 928 ? undefined 929 : node.asteriskToken, 930 visitNode(node.name, visitor, isPropertyName), 931 visitNode<Token<SyntaxKind.QuestionToken>>(/*questionToken*/ undefined, visitor, isToken), 932 /*typeParameters*/ undefined, 933 visitParameterList(node.parameters, parameterVisitor, context), 934 /*type*/ undefined, 935 enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator 936 ? transformAsyncGeneratorFunctionBody(node) 937 : transformFunctionBody(node) 938 ); 939 enclosingFunctionFlags = savedEnclosingFunctionFlags; 940 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 941 return updated; 942 } 943 944 function visitFunctionDeclaration(node: FunctionDeclaration) { 945 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 946 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 947 enclosingFunctionFlags = getFunctionFlags(node); 948 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 949 const updated = factory.updateFunctionDeclaration( 950 node, 951 enclosingFunctionFlags & FunctionFlags.Generator 952 ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifier) 953 : node.modifiers, 954 enclosingFunctionFlags & FunctionFlags.Async 955 ? undefined 956 : node.asteriskToken, 957 node.name, 958 /*typeParameters*/ undefined, 959 visitParameterList(node.parameters, parameterVisitor, context), 960 /*type*/ undefined, 961 enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator 962 ? transformAsyncGeneratorFunctionBody(node) 963 : transformFunctionBody(node) 964 ); 965 enclosingFunctionFlags = savedEnclosingFunctionFlags; 966 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 967 return updated; 968 } 969 970 function visitArrowFunction(node: ArrowFunction) { 971 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 972 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 973 enclosingFunctionFlags = getFunctionFlags(node); 974 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 975 const updated = factory.updateArrowFunction( 976 node, 977 node.modifiers, 978 /*typeParameters*/ undefined, 979 visitParameterList(node.parameters, parameterVisitor, context), 980 /*type*/ undefined, 981 node.equalsGreaterThanToken, 982 transformFunctionBody(node), 983 ); 984 enclosingFunctionFlags = savedEnclosingFunctionFlags; 985 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 986 return updated; 987 } 988 989 function visitFunctionExpression(node: FunctionExpression) { 990 const savedEnclosingFunctionFlags = enclosingFunctionFlags; 991 const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread; 992 enclosingFunctionFlags = getFunctionFlags(node); 993 parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node); 994 const updated = factory.updateFunctionExpression( 995 node, 996 enclosingFunctionFlags & FunctionFlags.Generator 997 ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifier) 998 : node.modifiers, 999 enclosingFunctionFlags & FunctionFlags.Async 1000 ? undefined 1001 : node.asteriskToken, 1002 node.name, 1003 /*typeParameters*/ undefined, 1004 visitParameterList(node.parameters, parameterVisitor, context), 1005 /*type*/ undefined, 1006 enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator 1007 ? transformAsyncGeneratorFunctionBody(node) 1008 : transformFunctionBody(node) 1009 ); 1010 enclosingFunctionFlags = savedEnclosingFunctionFlags; 1011 parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread; 1012 return updated; 1013 } 1014 1015 function transformAsyncGeneratorFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { 1016 resumeLexicalEnvironment(); 1017 const statements: Statement[] = []; 1018 const statementOffset = factory.copyPrologue(node.body!.statements, statements, /*ensureUseStrict*/ false, visitor); 1019 appendObjectRestAssignmentsIfNeeded(statements, node); 1020 1021 const savedCapturedSuperProperties = capturedSuperProperties; 1022 const savedHasSuperElementAccess = hasSuperElementAccess; 1023 capturedSuperProperties = new Set(); 1024 hasSuperElementAccess = false; 1025 1026 const returnStatement = factory.createReturnStatement( 1027 emitHelpers().createAsyncGeneratorHelper( 1028 factory.createFunctionExpression( 1029 /*modifiers*/ undefined, 1030 factory.createToken(SyntaxKind.AsteriskToken), 1031 node.name && factory.getGeneratedNameForNode(node.name), 1032 /*typeParameters*/ undefined, 1033 /*parameters*/ [], 1034 /*type*/ undefined, 1035 factory.updateBlock( 1036 node.body!, 1037 visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset) 1038 ) 1039 ), 1040 !!(hierarchyFacts & HierarchyFacts.HasLexicalThis) 1041 ) 1042 ); 1043 1044 // Minor optimization, emit `_super` helper to capture `super` access in an arrow. 1045 // This step isn't needed if we eventually transform this to ES5. 1046 const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync); 1047 1048 if (emitSuperHelpers) { 1049 enableSubstitutionForAsyncMethodsWithSuper(); 1050 const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); 1051 substitutedSuperAccessors[getNodeId(variableStatement)] = true; 1052 insertStatementsAfterStandardPrologue(statements, [variableStatement]); 1053 } 1054 1055 statements.push(returnStatement); 1056 1057 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 1058 const block = factory.updateBlock(node.body!, statements); 1059 1060 if (emitSuperHelpers && hasSuperElementAccess) { 1061 if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) { 1062 addEmitHelper(block, advancedAsyncSuperHelper); 1063 } 1064 else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) { 1065 addEmitHelper(block, asyncSuperHelper); 1066 } 1067 } 1068 1069 capturedSuperProperties = savedCapturedSuperProperties; 1070 hasSuperElementAccess = savedHasSuperElementAccess; 1071 1072 return block; 1073 } 1074 1075 function transformFunctionBody(node: FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | AccessorDeclaration): FunctionBody; 1076 function transformFunctionBody(node: ArrowFunction): ConciseBody; 1077 function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { 1078 resumeLexicalEnvironment(); 1079 let statementOffset = 0; 1080 const statements: Statement[] = []; 1081 const body = visitNode(node.body, visitor, isConciseBody) ?? factory.createBlock([]); 1082 if (isBlock(body)) { 1083 statementOffset = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor); 1084 } 1085 addRange(statements, appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node)); 1086 1087 const leadingStatements = endLexicalEnvironment(); 1088 if (statementOffset > 0 || some(statements) || some(leadingStatements)) { 1089 const block = factory.converters.convertToFunctionBlock(body, /*multiLine*/ true); 1090 insertStatementsAfterStandardPrologue(statements, leadingStatements); 1091 addRange(statements, block.statements.slice(statementOffset)); 1092 return factory.updateBlock(block, setTextRange(factory.createNodeArray(statements), block.statements)); 1093 } 1094 return body; 1095 } 1096 1097 function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined { 1098 let containsPrecedingObjectRestOrSpread = false; 1099 for (const parameter of node.parameters) { 1100 if (containsPrecedingObjectRestOrSpread) { 1101 if (isBindingPattern(parameter.name)) { 1102 // In cases where a binding pattern is simply '[]' or '{}', 1103 // we usually don't want to emit a var declaration; however, in the presence 1104 // of an initializer, we must emit that expression to preserve side effects. 1105 // 1106 // NOTE: see `insertDefaultValueAssignmentForBindingPattern` in es2015.ts 1107 if (parameter.name.elements.length > 0) { 1108 const declarations = flattenDestructuringBinding( 1109 parameter, 1110 visitor, 1111 context, 1112 FlattenLevel.All, 1113 factory.getGeneratedNameForNode(parameter)); 1114 if (some(declarations)) { 1115 const declarationList = factory.createVariableDeclarationList(declarations); 1116 const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList); 1117 setEmitFlags(statement, EmitFlags.CustomPrologue); 1118 statements = append(statements, statement); 1119 } 1120 } 1121 else if (parameter.initializer) { 1122 const name = factory.getGeneratedNameForNode(parameter); 1123 const initializer = visitNode(parameter.initializer, visitor, isExpression); 1124 const assignment = factory.createAssignment(name, initializer); 1125 const statement = factory.createExpressionStatement(assignment); 1126 setEmitFlags(statement, EmitFlags.CustomPrologue); 1127 statements = append(statements, statement); 1128 } 1129 } 1130 else if (parameter.initializer) { 1131 // Converts a parameter initializer into a function body statement, i.e.: 1132 // 1133 // function f(x = 1) { } 1134 // 1135 // becomes 1136 // 1137 // function f(x) { 1138 // if (typeof x === "undefined") { x = 1; } 1139 // } 1140 1141 const name = factory.cloneNode(parameter.name); 1142 setTextRange(name, parameter.name); 1143 setEmitFlags(name, EmitFlags.NoSourceMap); 1144 1145 const initializer = visitNode(parameter.initializer, visitor, isExpression); 1146 addEmitFlags(initializer, EmitFlags.NoSourceMap | EmitFlags.NoComments); 1147 1148 const assignment = factory.createAssignment(name, initializer); 1149 setTextRange(assignment, parameter); 1150 setEmitFlags(assignment, EmitFlags.NoComments); 1151 1152 const block = factory.createBlock([factory.createExpressionStatement(assignment)]); 1153 setTextRange(block, parameter); 1154 setEmitFlags(block, EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments); 1155 1156 const typeCheck = factory.createTypeCheck(factory.cloneNode(parameter.name), "undefined"); 1157 const statement = factory.createIfStatement(typeCheck, block); 1158 startOnNewLine(statement); 1159 setTextRange(statement, parameter); 1160 setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments); 1161 statements = append(statements, statement); 1162 } 1163 } 1164 else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { 1165 containsPrecedingObjectRestOrSpread = true; 1166 const declarations = flattenDestructuringBinding( 1167 parameter, 1168 visitor, 1169 context, 1170 FlattenLevel.ObjectRest, 1171 factory.getGeneratedNameForNode(parameter), 1172 /*doNotRecordTempVariablesInLine*/ false, 1173 /*skipInitializer*/ true, 1174 ); 1175 if (some(declarations)) { 1176 const declarationList = factory.createVariableDeclarationList(declarations); 1177 const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList); 1178 setEmitFlags(statement, EmitFlags.CustomPrologue); 1179 statements = append(statements, statement); 1180 } 1181 } 1182 } 1183 return statements; 1184 } 1185 1186 function enableSubstitutionForAsyncMethodsWithSuper() { 1187 if ((enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper) === 0) { 1188 enabledSubstitutions |= ESNextSubstitutionFlags.AsyncMethodsWithSuper; 1189 1190 // We need to enable substitutions for call, property access, and element access 1191 // if we need to rewrite super calls. 1192 context.enableSubstitution(SyntaxKind.CallExpression); 1193 context.enableSubstitution(SyntaxKind.PropertyAccessExpression); 1194 context.enableSubstitution(SyntaxKind.ElementAccessExpression); 1195 1196 // We need to be notified when entering and exiting declarations that bind super. 1197 context.enableEmitNotification(SyntaxKind.ClassDeclaration); 1198 context.enableEmitNotification(SyntaxKind.MethodDeclaration); 1199 context.enableEmitNotification(SyntaxKind.GetAccessor); 1200 context.enableEmitNotification(SyntaxKind.SetAccessor); 1201 context.enableEmitNotification(SyntaxKind.Constructor); 1202 // We need to be notified when entering the generated accessor arrow functions. 1203 context.enableEmitNotification(SyntaxKind.VariableStatement); 1204 } 1205 } 1206 1207 /** 1208 * Called by the printer just before a node is printed. 1209 * 1210 * @param hint A hint as to the intended usage of the node. 1211 * @param node The node to be printed. 1212 * @param emitCallback The callback used to emit the node. 1213 */ 1214 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { 1215 // If we need to support substitutions for `super` in an async method, 1216 // we should track it here. 1217 if (enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { 1218 const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync); 1219 if (superContainerFlags !== enclosingSuperContainerFlags) { 1220 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 1221 enclosingSuperContainerFlags = superContainerFlags; 1222 previousOnEmitNode(hint, node, emitCallback); 1223 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 1224 return; 1225 } 1226 } 1227 // Disable substitution in the generated super accessor itself. 1228 else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { 1229 const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; 1230 enclosingSuperContainerFlags = 0 as NodeCheckFlags; 1231 previousOnEmitNode(hint, node, emitCallback); 1232 enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags; 1233 return; 1234 } 1235 1236 previousOnEmitNode(hint, node, emitCallback); 1237 } 1238 1239 /** 1240 * Hooks node substitutions. 1241 * 1242 * @param hint The context for the emitter. 1243 * @param node The node to substitute. 1244 */ 1245 function onSubstituteNode(hint: EmitHint, node: Node) { 1246 node = previousOnSubstituteNode(hint, node); 1247 if (hint === EmitHint.Expression && enclosingSuperContainerFlags) { 1248 return substituteExpression(node as Expression); 1249 } 1250 return node; 1251 } 1252 1253 function substituteExpression(node: Expression) { 1254 switch (node.kind) { 1255 case SyntaxKind.PropertyAccessExpression: 1256 return substitutePropertyAccessExpression(node as PropertyAccessExpression); 1257 case SyntaxKind.ElementAccessExpression: 1258 return substituteElementAccessExpression(node as ElementAccessExpression); 1259 case SyntaxKind.CallExpression: 1260 return substituteCallExpression(node as CallExpression); 1261 } 1262 return node; 1263 } 1264 1265 function substitutePropertyAccessExpression(node: PropertyAccessExpression) { 1266 if (node.expression.kind === SyntaxKind.SuperKeyword) { 1267 return setTextRange( 1268 factory.createPropertyAccessExpression( 1269 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 1270 node.name), 1271 node 1272 ); 1273 } 1274 return node; 1275 } 1276 1277 function substituteElementAccessExpression(node: ElementAccessExpression) { 1278 if (node.expression.kind === SyntaxKind.SuperKeyword) { 1279 return createSuperElementAccessInAsyncMethod( 1280 node.argumentExpression, 1281 node 1282 ); 1283 } 1284 return node; 1285 } 1286 1287 function substituteCallExpression(node: CallExpression): Expression { 1288 const expression = node.expression; 1289 if (isSuperProperty(expression)) { 1290 const argumentExpression = isPropertyAccessExpression(expression) 1291 ? substitutePropertyAccessExpression(expression) 1292 : substituteElementAccessExpression(expression); 1293 return factory.createCallExpression( 1294 factory.createPropertyAccessExpression(argumentExpression, "call"), 1295 /*typeArguments*/ undefined, 1296 [ 1297 factory.createThis(), 1298 ...node.arguments 1299 ] 1300 ); 1301 } 1302 return node; 1303 } 1304 1305 function isSuperContainer(node: Node) { 1306 const kind = node.kind; 1307 return kind === SyntaxKind.ClassDeclaration 1308 || kind === SyntaxKind.Constructor 1309 || kind === SyntaxKind.MethodDeclaration 1310 || kind === SyntaxKind.GetAccessor 1311 || kind === SyntaxKind.SetAccessor; 1312 } 1313 1314 function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression { 1315 if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) { 1316 return setTextRange( 1317 factory.createPropertyAccessExpression( 1318 factory.createCallExpression( 1319 factory.createIdentifier("_superIndex"), 1320 /*typeArguments*/ undefined, 1321 [argumentExpression] 1322 ), 1323 "value" 1324 ), 1325 location 1326 ); 1327 } 1328 else { 1329 return setTextRange( 1330 factory.createCallExpression( 1331 factory.createIdentifier("_superIndex"), 1332 /*typeArguments*/ undefined, 1333 [argumentExpression] 1334 ), 1335 location 1336 ); 1337 } 1338 } 1339 } 1340} 1341