1/*@internal*/ 2namespace ts { 3 const enum ES2015SubstitutionFlags { 4 /** Enables substitutions for captured `this` */ 5 CapturedThis = 1 << 0, 6 /** Enables substitutions for block-scoped bindings. */ 7 BlockScopedBindings = 1 << 1, 8 } 9 10 /** 11 * If loop contains block scoped binding captured in some function then loop body is converted to a function. 12 * Lexical bindings declared in loop initializer will be passed into the loop body function as parameters, 13 * however if this binding is modified inside the body - this new value should be propagated back to the original binding. 14 * This is done by declaring new variable (out parameter holder) outside of the loop for every binding that is reassigned inside the body. 15 * On every iteration this variable is initialized with value of corresponding binding. 16 * At every point where control flow leaves the loop either explicitly (break/continue) or implicitly (at the end of loop body) 17 * we copy the value inside the loop to the out parameter holder. 18 * 19 * for (let x;;) { 20 * let a = 1; 21 * let b = () => a; 22 * x++ 23 * if (...) break; 24 * ... 25 * } 26 * 27 * will be converted to 28 * 29 * var out_x; 30 * var loop = function(x) { 31 * var a = 1; 32 * var b = function() { return a; } 33 * x++; 34 * if (...) return out_x = x, "break"; 35 * ... 36 * out_x = x; 37 * } 38 * for (var x;;) { 39 * out_x = x; 40 * var state = loop(x); 41 * x = out_x; 42 * if (state === "break") break; 43 * } 44 * 45 * NOTE: values to out parameters are not copies if loop is abrupted with 'return' - in this case this will end the entire enclosing function 46 * so nobody can observe this new value. 47 */ 48 interface LoopOutParameter { 49 flags: LoopOutParameterFlags; 50 originalName: Identifier; 51 outParamName: Identifier; 52 } 53 54 const enum LoopOutParameterFlags { 55 Body = 1 << 0, // Modified in the body of the iteration statement 56 Initializer = 1 << 1, // Set in the initializer of a ForStatement 57 } 58 59 const enum CopyDirection { 60 ToOriginal, 61 ToOutParameter 62 } 63 64 const enum Jump { 65 Break = 1 << 1, 66 Continue = 1 << 2, 67 Return = 1 << 3 68 } 69 70 interface ConvertedLoopState { 71 /* 72 * set of labels that occurred inside the converted loop 73 * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code 74 */ 75 labels?: ESMap<string, boolean>; 76 /* 77 * collection of labeled jumps that transfer control outside the converted loop. 78 * maps store association 'label -> labelMarker' where 79 * - label - value of label as it appear in code 80 * - label marker - return value that should be interpreted by calling code as 'jump to <label>' 81 */ 82 labeledNonLocalBreaks?: ESMap<string, string>; 83 labeledNonLocalContinues?: ESMap<string, string>; 84 85 /* 86 * set of non-labeled jumps that transfer control outside the converted loop 87 * used to emit dispatching logic in the caller of converted loop 88 */ 89 nonLocalJumps?: Jump; 90 91 /* 92 * set of non-labeled jumps that should be interpreted as local 93 * i.e. if converted loop contains normal loop or switch statement then inside this loop break should be treated as local jump 94 */ 95 allowedNonLabeledJumps?: Jump; 96 97 /* 98 * alias for 'arguments' object from the calling code stack frame 99 * i.e. 100 * for (let x;;) <statement that captures x in closure and uses 'arguments'> 101 * should be converted to 102 * var loop = function(x) { <code where 'arguments' is replaced with 'arguments_1'> } 103 * var arguments_1 = arguments 104 * for (var x;;) loop(x); 105 * otherwise semantics of the code will be different since 'arguments' inside converted loop body 106 * will refer to function that holds converted loop. 107 * This value is set on demand. 108 */ 109 argumentsName?: Identifier; 110 111 /* 112 * alias for 'this' from the calling code stack frame in case if this was used inside the converted loop 113 */ 114 thisName?: Identifier; 115 116 /* 117 * set to true if node contains lexical 'this' so we can mark function that wraps convered loop body as 'CapturedThis' for subsequent substitution. 118 */ 119 containsLexicalThis?: boolean; 120 121 /* 122 * list of non-block scoped variable declarations that appear inside converted loop 123 * such variable declarations should be moved outside the loop body 124 * for (let x;;) { 125 * var y = 1; 126 * ... 127 * } 128 * should be converted to 129 * var loop = function(x) { 130 * y = 1; 131 * ... 132 * } 133 * var y; 134 * for (var x;;) loop(x); 135 */ 136 hoistedLocalVariables?: Identifier[]; 137 138 conditionVariable?: Identifier; 139 140 loopParameters: ParameterDeclaration[]; 141 142 /** 143 * List of loop out parameters - detailed descripion can be found in the comment to LoopOutParameter 144 */ 145 loopOutParameters: LoopOutParameter[]; 146 } 147 148 type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement; 149 150 // Facts we track as we traverse the tree 151 const enum HierarchyFacts { 152 None = 0, 153 154 // 155 // Ancestor facts 156 // 157 158 Function = 1 << 0, // Enclosed in a non-arrow function 159 ArrowFunction = 1 << 1, // Enclosed in an arrow function 160 AsyncFunctionBody = 1 << 2, // Enclosed in an async function body 161 NonStaticClassElement = 1 << 3, // Enclosed in a non-static, non-async class element 162 CapturesThis = 1 << 4, // Enclosed in a function that captures the lexical 'this' (used in substitution) 163 ExportedVariableStatement = 1 << 5, // Enclosed in an exported variable statement in the current scope 164 TopLevel = 1 << 6, // Enclosing block-scoped container is a top-level container 165 Block = 1 << 7, // Enclosing block-scoped container is a Block 166 IterationStatement = 1 << 8, // Immediately enclosed in an IterationStatement 167 IterationStatementBlock = 1 << 9, // Enclosing Block is enclosed in an IterationStatement 168 IterationContainer = 1 << 10, // Enclosed in an outer IterationStatement 169 ForStatement = 1 << 11, // Enclosing block-scoped container is a ForStatement 170 ForInOrForOfStatement = 1 << 12, // Enclosing block-scoped container is a ForInStatement or ForOfStatement 171 ConstructorWithCapturedSuper = 1 << 13, // Enclosed in a constructor that captures 'this' for use with 'super' 172 // NOTE: do not add more ancestor flags without also updating AncestorFactsMask below. 173 // NOTE: when adding a new ancestor flag, be sure to update the subtree flags below. 174 175 // 176 // Ancestor masks 177 // 178 179 AncestorFactsMask = (ConstructorWithCapturedSuper << 1) - 1, 180 181 // We are always in *some* kind of block scope, but only specific block-scope containers are 182 // top-level or Blocks. 183 BlockScopeIncludes = None, 184 BlockScopeExcludes = TopLevel | Block | IterationStatement | IterationStatementBlock | ForStatement | ForInOrForOfStatement, 185 186 // A source file is a top-level block scope. 187 SourceFileIncludes = TopLevel, 188 SourceFileExcludes = BlockScopeExcludes & ~TopLevel | IterationContainer, 189 190 // Functions, methods, and accessors are both new lexical scopes and new block scopes. 191 FunctionIncludes = Function | TopLevel, 192 FunctionExcludes = BlockScopeExcludes & ~TopLevel | ArrowFunction | AsyncFunctionBody | CapturesThis | NonStaticClassElement | ConstructorWithCapturedSuper | IterationContainer, 193 194 AsyncFunctionBodyIncludes = FunctionIncludes | AsyncFunctionBody, 195 AsyncFunctionBodyExcludes = FunctionExcludes & ~NonStaticClassElement, 196 197 // Arrow functions are lexically scoped to their container, but are new block scopes. 198 ArrowFunctionIncludes = ArrowFunction | TopLevel, 199 ArrowFunctionExcludes = BlockScopeExcludes & ~TopLevel | ConstructorWithCapturedSuper, 200 201 // Constructors are both new lexical scopes and new block scopes. Constructors are also 202 // always considered non-static members of a class. 203 ConstructorIncludes = FunctionIncludes | NonStaticClassElement, 204 ConstructorExcludes = FunctionExcludes & ~NonStaticClassElement, 205 206 // 'do' and 'while' statements are not block scopes. We track that the subtree is contained 207 // within an IterationStatement to indicate whether the embedded statement is an 208 // IterationStatementBlock. 209 DoOrWhileStatementIncludes = IterationStatement | IterationContainer, 210 DoOrWhileStatementExcludes = None, 211 212 // 'for' statements are new block scopes and have special handling for 'let' declarations. 213 ForStatementIncludes = IterationStatement | ForStatement | IterationContainer, 214 ForStatementExcludes = BlockScopeExcludes & ~ForStatement, 215 216 // 'for-in' and 'for-of' statements are new block scopes and have special handling for 217 // 'let' declarations. 218 ForInOrForOfStatementIncludes = IterationStatement | ForInOrForOfStatement | IterationContainer, 219 ForInOrForOfStatementExcludes = BlockScopeExcludes & ~ForInOrForOfStatement, 220 221 // Blocks (other than function bodies) are new block scopes. 222 BlockIncludes = Block, 223 BlockExcludes = BlockScopeExcludes & ~Block, 224 225 IterationStatementBlockIncludes = IterationStatementBlock, 226 IterationStatementBlockExcludes = BlockScopeExcludes, 227 228 // 229 // Subtree facts 230 // 231 232 NewTarget = 1 << 14, // Contains a 'new.target' meta-property 233 CapturedLexicalThis = 1 << 15, // Contains a lexical `this` reference captured by an arrow function. 234 235 // 236 // Subtree masks 237 // 238 239 SubtreeFactsMask = ~AncestorFactsMask, 240 241 ArrowFunctionSubtreeExcludes = None, 242 FunctionSubtreeExcludes = NewTarget | CapturedLexicalThis, 243 } 244 245 export function transformES2015(context: TransformationContext) { 246 const { 247 factory, 248 getEmitHelperFactory: emitHelpers, 249 startLexicalEnvironment, 250 resumeLexicalEnvironment, 251 endLexicalEnvironment, 252 hoistVariableDeclaration, 253 } = context; 254 255 const compilerOptions = context.getCompilerOptions(); 256 const resolver = context.getEmitResolver(); 257 const previousOnSubstituteNode = context.onSubstituteNode; 258 const previousOnEmitNode = context.onEmitNode; 259 context.onEmitNode = onEmitNode; 260 context.onSubstituteNode = onSubstituteNode; 261 262 let currentSourceFile: SourceFile; 263 let currentText: string; 264 let hierarchyFacts: HierarchyFacts; 265 let taggedTemplateStringDeclarations: VariableDeclaration[]; 266 267 function recordTaggedTemplateString(temp: Identifier) { 268 taggedTemplateStringDeclarations = append( 269 taggedTemplateStringDeclarations, 270 factory.createVariableDeclaration(temp)); 271 } 272 273 /** 274 * Used to track if we are emitting body of the converted loop 275 */ 276 let convertedLoopState: ConvertedLoopState | undefined; 277 278 /** 279 * Keeps track of whether substitutions have been enabled for specific cases. 280 * They are persisted between each SourceFile transformation and should not 281 * be reset. 282 */ 283 let enabledSubstitutions: ES2015SubstitutionFlags; 284 285 return chainBundle(context, transformSourceFile); 286 287 function transformSourceFile(node: SourceFile) { 288 if (node.isDeclarationFile) { 289 return node; 290 } 291 292 currentSourceFile = node; 293 currentText = node.text; 294 295 const visited = visitSourceFile(node); 296 addEmitHelpers(visited, context.readEmitHelpers()); 297 298 currentSourceFile = undefined!; 299 currentText = undefined!; 300 taggedTemplateStringDeclarations = undefined!; 301 hierarchyFacts = HierarchyFacts.None; 302 return visited; 303 } 304 305 /** 306 * Sets the `HierarchyFacts` for this node prior to visiting this node's subtree, returning the facts set prior to modification. 307 * @param excludeFacts The existing `HierarchyFacts` to reset before visiting the subtree. 308 * @param includeFacts The new `HierarchyFacts` to set before visiting the subtree. 309 */ 310 function enterSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) { 311 const ancestorFacts = hierarchyFacts; 312 hierarchyFacts = (hierarchyFacts & ~excludeFacts | includeFacts) & HierarchyFacts.AncestorFactsMask; 313 return ancestorFacts; 314 } 315 316 /** 317 * Restores the `HierarchyFacts` for this node's ancestor after visiting this node's 318 * subtree, propagating specific facts from the subtree. 319 * @param ancestorFacts The `HierarchyFacts` of the ancestor to restore after visiting the subtree. 320 * @param excludeFacts The existing `HierarchyFacts` of the subtree that should not be propagated. 321 * @param includeFacts The new `HierarchyFacts` of the subtree that should be propagated. 322 */ 323 function exitSubtree(ancestorFacts: HierarchyFacts, excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) { 324 hierarchyFacts = (hierarchyFacts & ~excludeFacts | includeFacts) & HierarchyFacts.SubtreeFactsMask | ancestorFacts; 325 } 326 327 function isReturnVoidStatementInConstructorWithCapturedSuper(node: Node): boolean { 328 return (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper) !== 0 329 && node.kind === SyntaxKind.ReturnStatement 330 && !(<ReturnStatement>node).expression; 331 } 332 333 function isOrMayContainReturnCompletion(node: Node) { 334 return node.transformFlags & TransformFlags.ContainsHoistedDeclarationOrCompletion 335 && (isReturnStatement(node) 336 || isIfStatement(node) 337 || isWithStatement(node) 338 || isSwitchStatement(node) 339 || isCaseBlock(node) 340 || isCaseClause(node) 341 || isDefaultClause(node) 342 || isTryStatement(node) 343 || isCatchClause(node) 344 || isLabeledStatement(node) 345 || isIterationStatement(node, /*lookInLabeledStatements*/ false) 346 || isBlock(node)); 347 } 348 349 function shouldVisitNode(node: Node): boolean { 350 return (node.transformFlags & TransformFlags.ContainsES2015) !== 0 351 || convertedLoopState !== undefined 352 || (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && isOrMayContainReturnCompletion(node)) 353 || (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatement(node)) 354 || (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) !== 0; 355 } 356 357 function visitor(node: Node): VisitResult<Node> { 358 return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ false) : node; 359 } 360 361 function visitorWithUnusedExpressionResult(node: Node): VisitResult<Node> { 362 return shouldVisitNode(node) ? visitorWorker(node, /*expressionResultIsUnused*/ true) : node; 363 } 364 365 function callExpressionVisitor(node: Node): VisitResult<Node> { 366 if (node.kind === SyntaxKind.SuperKeyword) { 367 return visitSuperKeyword(/*isExpressionOfCall*/ true); 368 } 369 return visitor(node); 370 } 371 372 function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult<Node> { 373 switch (node.kind) { 374 case SyntaxKind.StaticKeyword: 375 return undefined; // elide static keyword 376 377 case SyntaxKind.ClassDeclaration: 378 return visitClassDeclaration(<ClassDeclaration>node); 379 380 case SyntaxKind.ClassExpression: 381 return visitClassExpression(<ClassExpression>node); 382 383 case SyntaxKind.Parameter: 384 return visitParameter(<ParameterDeclaration>node); 385 386 case SyntaxKind.FunctionDeclaration: 387 return visitFunctionDeclaration(<FunctionDeclaration>node); 388 389 case SyntaxKind.ArrowFunction: 390 return visitArrowFunction(<ArrowFunction>node); 391 392 case SyntaxKind.FunctionExpression: 393 return visitFunctionExpression(<FunctionExpression>node); 394 395 case SyntaxKind.VariableDeclaration: 396 return visitVariableDeclaration(<VariableDeclaration>node); 397 398 case SyntaxKind.Identifier: 399 return visitIdentifier(<Identifier>node); 400 401 case SyntaxKind.VariableDeclarationList: 402 return visitVariableDeclarationList(<VariableDeclarationList>node); 403 404 case SyntaxKind.SwitchStatement: 405 return visitSwitchStatement(<SwitchStatement>node); 406 407 case SyntaxKind.CaseBlock: 408 return visitCaseBlock(<CaseBlock>node); 409 410 case SyntaxKind.Block: 411 return visitBlock(<Block>node, /*isFunctionBody*/ false); 412 413 case SyntaxKind.BreakStatement: 414 case SyntaxKind.ContinueStatement: 415 return visitBreakOrContinueStatement(<BreakOrContinueStatement>node); 416 417 case SyntaxKind.LabeledStatement: 418 return visitLabeledStatement(<LabeledStatement>node); 419 420 case SyntaxKind.DoStatement: 421 case SyntaxKind.WhileStatement: 422 return visitDoOrWhileStatement(<DoStatement | WhileStatement>node, /*outermostLabeledStatement*/ undefined); 423 424 case SyntaxKind.ForStatement: 425 return visitForStatement(<ForStatement>node, /*outermostLabeledStatement*/ undefined); 426 427 case SyntaxKind.ForInStatement: 428 return visitForInStatement(<ForInStatement>node, /*outermostLabeledStatement*/ undefined); 429 430 case SyntaxKind.ForOfStatement: 431 return visitForOfStatement(<ForOfStatement>node, /*outermostLabeledStatement*/ undefined); 432 433 case SyntaxKind.ExpressionStatement: 434 return visitExpressionStatement(<ExpressionStatement>node); 435 436 case SyntaxKind.ObjectLiteralExpression: 437 return visitObjectLiteralExpression(<ObjectLiteralExpression>node); 438 439 case SyntaxKind.CatchClause: 440 return visitCatchClause(<CatchClause>node); 441 442 case SyntaxKind.ShorthandPropertyAssignment: 443 return visitShorthandPropertyAssignment(<ShorthandPropertyAssignment>node); 444 445 case SyntaxKind.ComputedPropertyName: 446 return visitComputedPropertyName(<ComputedPropertyName>node); 447 448 case SyntaxKind.ArrayLiteralExpression: 449 return visitArrayLiteralExpression(<ArrayLiteralExpression>node); 450 451 case SyntaxKind.CallExpression: 452 return visitCallExpression(<CallExpression>node); 453 454 case SyntaxKind.NewExpression: 455 return visitNewExpression(<NewExpression>node); 456 457 case SyntaxKind.ParenthesizedExpression: 458 return visitParenthesizedExpression(<ParenthesizedExpression>node, expressionResultIsUnused); 459 460 case SyntaxKind.BinaryExpression: 461 return visitBinaryExpression(<BinaryExpression>node, expressionResultIsUnused); 462 463 case SyntaxKind.CommaListExpression: 464 return visitCommaListExpression(<CommaListExpression>node, expressionResultIsUnused); 465 466 case SyntaxKind.NoSubstitutionTemplateLiteral: 467 case SyntaxKind.TemplateHead: 468 case SyntaxKind.TemplateMiddle: 469 case SyntaxKind.TemplateTail: 470 return visitTemplateLiteral(<LiteralExpression>node); 471 472 case SyntaxKind.StringLiteral: 473 return visitStringLiteral(<StringLiteral>node); 474 475 case SyntaxKind.NumericLiteral: 476 return visitNumericLiteral(<NumericLiteral>node); 477 478 case SyntaxKind.TaggedTemplateExpression: 479 return visitTaggedTemplateExpression(<TaggedTemplateExpression>node); 480 481 case SyntaxKind.TemplateExpression: 482 return visitTemplateExpression(<TemplateExpression>node); 483 484 case SyntaxKind.YieldExpression: 485 return visitYieldExpression(<YieldExpression>node); 486 487 case SyntaxKind.SpreadElement: 488 return visitSpreadElement(<SpreadElement>node); 489 490 case SyntaxKind.SuperKeyword: 491 return visitSuperKeyword(/*isExpressionOfCall*/ false); 492 493 case SyntaxKind.ThisKeyword: 494 return visitThisKeyword(node); 495 496 case SyntaxKind.MetaProperty: 497 return visitMetaProperty(<MetaProperty>node); 498 499 case SyntaxKind.MethodDeclaration: 500 return visitMethodDeclaration(<MethodDeclaration>node); 501 502 case SyntaxKind.GetAccessor: 503 case SyntaxKind.SetAccessor: 504 return visitAccessorDeclaration(<AccessorDeclaration>node); 505 506 case SyntaxKind.VariableStatement: 507 return visitVariableStatement(<VariableStatement>node); 508 509 case SyntaxKind.ReturnStatement: 510 return visitReturnStatement(<ReturnStatement>node); 511 512 case SyntaxKind.VoidExpression: 513 return visitVoidExpression(node as VoidExpression); 514 515 default: 516 return visitEachChild(node, visitor, context); 517 } 518 } 519 520 function visitSourceFile(node: SourceFile): SourceFile { 521 const ancestorFacts = enterSubtree(HierarchyFacts.SourceFileExcludes, HierarchyFacts.SourceFileIncludes); 522 const prologue: Statement[] = []; 523 const statements: Statement[] = []; 524 startLexicalEnvironment(); 525 const statementOffset = factory.copyPrologue(node.statements, prologue, /*ensureUseStrict*/ false, visitor); 526 addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); 527 if (taggedTemplateStringDeclarations) { 528 statements.push( 529 factory.createVariableStatement(/*modifiers*/ undefined, 530 factory.createVariableDeclarationList(taggedTemplateStringDeclarations))); 531 } 532 factory.mergeLexicalEnvironment(prologue, endLexicalEnvironment()); 533 insertCaptureThisForNodeIfNeeded(prologue, node); 534 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 535 return factory.updateSourceFile( 536 node, 537 setTextRange(factory.createNodeArray(concatenate(prologue, statements)), node.statements) 538 ); 539 } 540 541 function visitSwitchStatement(node: SwitchStatement): SwitchStatement { 542 if (convertedLoopState !== undefined) { 543 const savedAllowedNonLabeledJumps = convertedLoopState.allowedNonLabeledJumps; 544 // for switch statement allow only non-labeled break 545 convertedLoopState.allowedNonLabeledJumps! |= Jump.Break; 546 const result = visitEachChild(node, visitor, context); 547 convertedLoopState.allowedNonLabeledJumps = savedAllowedNonLabeledJumps; 548 return result; 549 } 550 return visitEachChild(node, visitor, context); 551 } 552 553 function visitCaseBlock(node: CaseBlock): CaseBlock { 554 const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes); 555 const updated = visitEachChild(node, visitor, context); 556 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 557 return updated; 558 } 559 560 function returnCapturedThis(node: Node): ReturnStatement { 561 return setOriginalNode(factory.createReturnStatement(factory.createUniqueName("_this", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel)), node); 562 } 563 564 function visitReturnStatement(node: ReturnStatement): Statement { 565 if (convertedLoopState) { 566 convertedLoopState.nonLocalJumps! |= Jump.Return; 567 if (isReturnVoidStatementInConstructorWithCapturedSuper(node)) { 568 node = returnCapturedThis(node); 569 } 570 return factory.createReturnStatement( 571 factory.createObjectLiteralExpression( 572 [ 573 factory.createPropertyAssignment( 574 factory.createIdentifier("value"), 575 node.expression 576 ? visitNode(node.expression, visitor, isExpression) 577 : factory.createVoidZero() 578 ) 579 ] 580 ) 581 ); 582 } 583 else if (isReturnVoidStatementInConstructorWithCapturedSuper(node)) { 584 return returnCapturedThis(node); 585 } 586 return visitEachChild(node, visitor, context); 587 } 588 589 function visitThisKeyword(node: Node): Node { 590 if (hierarchyFacts & HierarchyFacts.ArrowFunction) { 591 hierarchyFacts |= HierarchyFacts.CapturedLexicalThis; 592 } 593 if (convertedLoopState) { 594 if (hierarchyFacts & HierarchyFacts.ArrowFunction) { 595 // if the enclosing function is an ArrowFunction then we use the captured 'this' keyword. 596 convertedLoopState.containsLexicalThis = true; 597 return node; 598 } 599 return convertedLoopState.thisName || (convertedLoopState.thisName = factory.createUniqueName("this")); 600 } 601 return node; 602 } 603 604 function visitVoidExpression(node: VoidExpression): Expression { 605 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 606 } 607 608 function visitIdentifier(node: Identifier): Identifier { 609 if (!convertedLoopState) { 610 return node; 611 } 612 if (resolver.isArgumentsLocalBinding(node)) { 613 return convertedLoopState.argumentsName || (convertedLoopState.argumentsName = factory.createUniqueName("arguments")); 614 } 615 return node; 616 } 617 618 function visitBreakOrContinueStatement(node: BreakOrContinueStatement): Statement { 619 if (convertedLoopState) { 620 // check if we can emit break/continue as is 621 // it is possible if either 622 // - break/continue is labeled and label is located inside the converted loop 623 // - break/continue is non-labeled and located in non-converted loop/switch statement 624 const jump = node.kind === SyntaxKind.BreakStatement ? Jump.Break : Jump.Continue; 625 const canUseBreakOrContinue = 626 (node.label && convertedLoopState.labels && convertedLoopState.labels.get(idText(node.label))) || 627 (!node.label && (convertedLoopState.allowedNonLabeledJumps! & jump)); 628 629 if (!canUseBreakOrContinue) { 630 let labelMarker: string; 631 const label = node.label; 632 if (!label) { 633 if (node.kind === SyntaxKind.BreakStatement) { 634 convertedLoopState.nonLocalJumps! |= Jump.Break; 635 labelMarker = "break"; 636 } 637 else { 638 convertedLoopState.nonLocalJumps! |= Jump.Continue; 639 // note: return value is emitted only to simplify debugging, call to converted loop body does not do any dispatching on it. 640 labelMarker = "continue"; 641 } 642 } 643 else { 644 if (node.kind === SyntaxKind.BreakStatement) { 645 labelMarker = `break-${label.escapedText}`; 646 setLabeledJump(convertedLoopState, /*isBreak*/ true, idText(label), labelMarker); 647 } 648 else { 649 labelMarker = `continue-${label.escapedText}`; 650 setLabeledJump(convertedLoopState, /*isBreak*/ false, idText(label), labelMarker); 651 } 652 } 653 let returnExpression: Expression = factory.createStringLiteral(labelMarker); 654 if (convertedLoopState.loopOutParameters.length) { 655 const outParams = convertedLoopState.loopOutParameters; 656 let expr: Expression | undefined; 657 for (let i = 0; i < outParams.length; i++) { 658 const copyExpr = copyOutParameter(outParams[i], CopyDirection.ToOutParameter); 659 if (i === 0) { 660 expr = copyExpr; 661 } 662 else { 663 expr = factory.createBinaryExpression(expr!, SyntaxKind.CommaToken, copyExpr); 664 } 665 } 666 returnExpression = factory.createBinaryExpression(expr!, SyntaxKind.CommaToken, returnExpression); 667 } 668 return factory.createReturnStatement(returnExpression); 669 } 670 } 671 return visitEachChild(node, visitor, context); 672 } 673 674 /** 675 * Visits a ClassDeclaration and transforms it into a variable statement. 676 * 677 * @param node A ClassDeclaration node. 678 */ 679 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 680 // [source] 681 // class C { } 682 // 683 // [output] 684 // var C = (function () { 685 // function C() { 686 // } 687 // return C; 688 // }()); 689 690 const variable = factory.createVariableDeclaration( 691 factory.getLocalName(node, /*allowComments*/ true), 692 /*exclamationToken*/ undefined, 693 /*type*/ undefined, 694 transformClassLikeDeclarationToExpression(node) 695 ); 696 697 setOriginalNode(variable, node); 698 699 const statements: Statement[] = []; 700 const statement = factory.createVariableStatement(/*modifiers*/ undefined, factory.createVariableDeclarationList([variable])); 701 702 setOriginalNode(statement, node); 703 setTextRange(statement, node); 704 startOnNewLine(statement); 705 statements.push(statement); 706 707 // Add an `export default` statement for default exports (for `--target es5 --module es6`) 708 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 709 const exportStatement = hasSyntacticModifier(node, ModifierFlags.Default) 710 ? factory.createExportDefault(factory.getLocalName(node)) 711 : factory.createExternalModuleExport(factory.getLocalName(node)); 712 713 setOriginalNode(exportStatement, statement); 714 statements.push(exportStatement); 715 } 716 717 const emitFlags = getEmitFlags(node); 718 if ((emitFlags & EmitFlags.HasEndOfDeclarationMarker) === 0) { 719 // Add a DeclarationMarker as a marker for the end of the declaration 720 statements.push(factory.createEndOfDeclarationMarker(node)); 721 setEmitFlags(statement, emitFlags | EmitFlags.HasEndOfDeclarationMarker); 722 } 723 724 return singleOrMany(statements); 725 } 726 727 /** 728 * Visits a ClassExpression and transforms it into an expression. 729 * 730 * @param node A ClassExpression node. 731 */ 732 function visitClassExpression(node: ClassExpression): Expression { 733 // [source] 734 // C = class { } 735 // 736 // [output] 737 // C = (function () { 738 // function class_1() { 739 // } 740 // return class_1; 741 // }()) 742 743 return transformClassLikeDeclarationToExpression(node); 744 } 745 746 /** 747 * Transforms a ClassExpression or ClassDeclaration into an expression. 748 * 749 * @param node A ClassExpression or ClassDeclaration node. 750 */ 751 function transformClassLikeDeclarationToExpression(node: ClassExpression | ClassDeclaration): Expression { 752 // [source] 753 // class C extends D { 754 // constructor() {} 755 // method() {} 756 // get prop() {} 757 // set prop(v) {} 758 // } 759 // 760 // [output] 761 // (function (_super) { 762 // __extends(C, _super); 763 // function C() { 764 // } 765 // C.prototype.method = function () {} 766 // Object.defineProperty(C.prototype, "prop", { 767 // get: function() {}, 768 // set: function() {}, 769 // enumerable: true, 770 // configurable: true 771 // }); 772 // return C; 773 // }(D)) 774 775 if (node.name) { 776 enableSubstitutionsForBlockScopedBindings(); 777 } 778 779 const extendsClauseElement = getClassExtendsHeritageElement(node); 780 const classFunction = factory.createFunctionExpression( 781 /*modifiers*/ undefined, 782 /*asteriskToken*/ undefined, 783 /*name*/ undefined, 784 /*typeParameters*/ undefined, 785 extendsClauseElement ? [factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel))] : [], 786 /*type*/ undefined, 787 transformClassBody(node, extendsClauseElement) 788 ); 789 790 // To preserve the behavior of the old emitter, we explicitly indent 791 // the body of the function here if it was requested in an earlier 792 // transformation. 793 setEmitFlags(classFunction, (getEmitFlags(node) & EmitFlags.Indented) | EmitFlags.ReuseTempVariableScope); 794 795 // "inner" and "outer" below are added purely to preserve source map locations from 796 // the old emitter 797 const inner = factory.createPartiallyEmittedExpression(classFunction); 798 setTextRangeEnd(inner, node.end); 799 setEmitFlags(inner, EmitFlags.NoComments); 800 801 const outer = factory.createPartiallyEmittedExpression(inner); 802 setTextRangeEnd(outer, skipTrivia(currentText, node.pos)); 803 setEmitFlags(outer, EmitFlags.NoComments); 804 805 const result = factory.createParenthesizedExpression( 806 factory.createCallExpression( 807 outer, 808 /*typeArguments*/ undefined, 809 extendsClauseElement 810 ? [visitNode(extendsClauseElement.expression, visitor, isExpression)] 811 : [] 812 ) 813 ); 814 addSyntheticLeadingComment(result, SyntaxKind.MultiLineCommentTrivia, "* @class "); 815 return result; 816 } 817 818 /** 819 * Transforms a ClassExpression or ClassDeclaration into a function body. 820 * 821 * @param node A ClassExpression or ClassDeclaration node. 822 * @param extendsClauseElement The expression for the class `extends` clause. 823 */ 824 function transformClassBody(node: ClassExpression | ClassDeclaration, extendsClauseElement: ExpressionWithTypeArguments | undefined): Block { 825 const statements: Statement[] = []; 826 const name = factory.getInternalName(node); 827 const constructorLikeName = isIdentifierANonContextualKeyword(name) ? factory.getGeneratedNameForNode(name) : name; 828 startLexicalEnvironment(); 829 addExtendsHelperIfNeeded(statements, node, extendsClauseElement); 830 addConstructor(statements, node, constructorLikeName, extendsClauseElement); 831 addClassMembers(statements, node); 832 833 // Create a synthetic text range for the return statement. 834 const closingBraceLocation = createTokenRange(skipTrivia(currentText, node.members.end), SyntaxKind.CloseBraceToken); 835 836 // The following partially-emitted expression exists purely to align our sourcemap 837 // emit with the original emitter. 838 const outer = factory.createPartiallyEmittedExpression(constructorLikeName); 839 setTextRangeEnd(outer, closingBraceLocation.end); 840 setEmitFlags(outer, EmitFlags.NoComments); 841 842 const statement = factory.createReturnStatement(outer); 843 setTextRangePos(statement, closingBraceLocation.pos); 844 setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps); 845 statements.push(statement); 846 847 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 848 849 const block = factory.createBlock(setTextRange(factory.createNodeArray(statements), /*location*/ node.members), /*multiLine*/ true); 850 setEmitFlags(block, EmitFlags.NoComments); 851 return block; 852 } 853 854 /** 855 * Adds a call to the `__extends` helper if needed for a class. 856 * 857 * @param statements The statements of the class body function. 858 * @param node The ClassExpression or ClassDeclaration node. 859 * @param extendsClauseElement The expression for the class `extends` clause. 860 */ 861 function addExtendsHelperIfNeeded(statements: Statement[], node: ClassExpression | ClassDeclaration, extendsClauseElement: ExpressionWithTypeArguments | undefined): void { 862 if (extendsClauseElement) { 863 statements.push( 864 setTextRange( 865 factory.createExpressionStatement( 866 emitHelpers().createExtendsHelper(factory.getInternalName(node)) 867 ), 868 /*location*/ extendsClauseElement 869 ) 870 ); 871 } 872 } 873 874 /** 875 * Adds the constructor of the class to a class body function. 876 * 877 * @param statements The statements of the class body function. 878 * @param node The ClassExpression or ClassDeclaration node. 879 * @param extendsClauseElement The expression for the class `extends` clause. 880 */ 881 function addConstructor(statements: Statement[], node: ClassExpression | ClassDeclaration, name: Identifier, extendsClauseElement: ExpressionWithTypeArguments | undefined): void { 882 const savedConvertedLoopState = convertedLoopState; 883 convertedLoopState = undefined; 884 const ancestorFacts = enterSubtree(HierarchyFacts.ConstructorExcludes, HierarchyFacts.ConstructorIncludes); 885 const constructor = getFirstConstructorWithBody(node); 886 const hasSynthesizedSuper = hasSynthesizedDefaultSuperCall(constructor, extendsClauseElement !== undefined); 887 const constructorFunction = factory.createFunctionDeclaration( 888 /*decorators*/ undefined, 889 /*modifiers*/ undefined, 890 /*asteriskToken*/ undefined, 891 name, 892 /*typeParameters*/ undefined, 893 transformConstructorParameters(constructor, hasSynthesizedSuper), 894 /*type*/ undefined, 895 transformConstructorBody(constructor, node, extendsClauseElement, hasSynthesizedSuper) 896 ); 897 898 setTextRange(constructorFunction, constructor || node); 899 if (extendsClauseElement) { 900 setEmitFlags(constructorFunction, EmitFlags.CapturesThis); 901 } 902 903 statements.push(constructorFunction); 904 exitSubtree(ancestorFacts, HierarchyFacts.FunctionSubtreeExcludes, HierarchyFacts.None); 905 convertedLoopState = savedConvertedLoopState; 906 } 907 908 /** 909 * Transforms the parameters of the constructor declaration of a class. 910 * 911 * @param constructor The constructor for the class. 912 * @param hasSynthesizedSuper A value indicating whether the constructor starts with a 913 * synthesized `super` call. 914 */ 915 function transformConstructorParameters(constructor: ConstructorDeclaration | undefined, hasSynthesizedSuper: boolean) { 916 // If the TypeScript transformer needed to synthesize a constructor for property 917 // initializers, it would have also added a synthetic `...args` parameter and 918 // `super` call. 919 // If this is the case, we do not include the synthetic `...args` parameter and 920 // will instead use the `arguments` object in ES5/3. 921 return visitParameterList(constructor && !hasSynthesizedSuper ? constructor.parameters : undefined, visitor, context) 922 || <ParameterDeclaration[]>[]; 923 } 924 925 function createDefaultConstructorBody(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { 926 // We must be here because the user didn't write a constructor 927 // but we needed to call 'super(...args)' anyway as per 14.5.14 of the ES2016 spec. 928 // If that's the case we can just immediately return the result of a 'super()' call. 929 const statements: Statement[] = []; 930 resumeLexicalEnvironment(); 931 factory.mergeLexicalEnvironment(statements, endLexicalEnvironment()); 932 933 if (isDerivedClass) { 934 // return _super !== null && _super.apply(this, arguments) || this; 935 statements.push(factory.createReturnStatement(createDefaultSuperCallOrThis())); 936 } 937 938 const statementsArray = factory.createNodeArray(statements); 939 setTextRange(statementsArray, node.members); 940 941 const block = factory.createBlock(statementsArray, /*multiLine*/ true); 942 setTextRange(block, node); 943 setEmitFlags(block, EmitFlags.NoComments); 944 return block; 945 } 946 947 /** 948 * Transforms the body of a constructor declaration of a class. 949 * 950 * @param constructor The constructor for the class. 951 * @param node The node which contains the constructor. 952 * @param extendsClauseElement The expression for the class `extends` clause. 953 * @param hasSynthesizedSuper A value indicating whether the constructor starts with a 954 * synthesized `super` call. 955 */ 956 function transformConstructorBody(constructor: ConstructorDeclaration & { body: FunctionBody } | undefined, node: ClassDeclaration | ClassExpression, extendsClauseElement: ExpressionWithTypeArguments | undefined, hasSynthesizedSuper: boolean) { 957 // determine whether the class is known syntactically to be a derived class (e.g. a 958 // class that extends a value that is not syntactically known to be `null`). 959 const isDerivedClass = !!extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword; 960 961 // When the subclass does not have a constructor, we synthesize a *default* constructor using the following 962 // representation: 963 // 964 // ``` 965 // // es2015 (source) 966 // class C extends Base { } 967 // 968 // // es5 (transformed) 969 // var C = (function (_super) { 970 // function C() { 971 // return _super.apply(this, arguments) || this; 972 // } 973 // return C; 974 // })(Base); 975 // ``` 976 if (!constructor) return createDefaultConstructorBody(node, isDerivedClass); 977 978 // The prologue will contain all leading standard and custom prologue statements added by this transform 979 const prologue: Statement[] = []; 980 const statements: Statement[] = []; 981 resumeLexicalEnvironment(); 982 983 // If a super call has already been synthesized, 984 // we're going to assume that we should just transform everything after that. 985 // The assumption is that no prior step in the pipeline has added any prologue directives. 986 let statementOffset = 0; 987 if (!hasSynthesizedSuper) statementOffset = factory.copyStandardPrologue(constructor.body.statements, prologue, /*ensureUseStrict*/ false); 988 addDefaultValueAssignmentsIfNeeded(statements, constructor); 989 addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); 990 if (!hasSynthesizedSuper) statementOffset = factory.copyCustomPrologue(constructor.body.statements, statements, statementOffset, visitor); 991 992 // If the first statement is a call to `super()`, visit the statement directly 993 let superCallExpression: Expression | undefined; 994 if (hasSynthesizedSuper) { 995 superCallExpression = createDefaultSuperCallOrThis(); 996 } 997 else if (isDerivedClass && statementOffset < constructor.body.statements.length) { 998 const firstStatement = constructor.body.statements[statementOffset]; 999 if (isExpressionStatement(firstStatement) && isSuperCall(firstStatement.expression)) { 1000 superCallExpression = visitImmediateSuperCallInBody(firstStatement.expression); 1001 } 1002 } 1003 1004 if (superCallExpression) { 1005 hierarchyFacts |= HierarchyFacts.ConstructorWithCapturedSuper; 1006 statementOffset++; // skip this statement, we will add it after visiting the rest of the body. 1007 } 1008 1009 // visit the remaining statements 1010 addRange(statements, visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset)); 1011 1012 factory.mergeLexicalEnvironment(prologue, endLexicalEnvironment()); 1013 insertCaptureNewTargetIfNeeded(prologue, constructor, /*copyOnWrite*/ false); 1014 1015 if (isDerivedClass) { 1016 if (superCallExpression && statementOffset === constructor.body.statements.length && !(constructor.body.transformFlags & TransformFlags.ContainsLexicalThis)) { 1017 // If the subclass constructor does *not* contain `this` and *ends* with a `super()` call, we will use the 1018 // following representation: 1019 // 1020 // ``` 1021 // // es2015 (source) 1022 // class C extends Base { 1023 // constructor() { 1024 // super("foo"); 1025 // } 1026 // } 1027 // 1028 // // es5 (transformed) 1029 // var C = (function (_super) { 1030 // function C() { 1031 // return _super.call(this, "foo") || this; 1032 // } 1033 // return C; 1034 // })(Base); 1035 // ``` 1036 const superCall = cast(cast(superCallExpression, isBinaryExpression).left, isCallExpression); 1037 const returnStatement = factory.createReturnStatement(superCallExpression); 1038 setCommentRange(returnStatement, getCommentRange(superCall)); 1039 setEmitFlags(superCall, EmitFlags.NoComments); 1040 statements.push(returnStatement); 1041 } 1042 else { 1043 // Otherwise, we will use the following transformed representation for calls to `super()` in a constructor: 1044 // 1045 // ``` 1046 // // es2015 (source) 1047 // class C extends Base { 1048 // constructor() { 1049 // super("foo"); 1050 // this.x = 1; 1051 // } 1052 // } 1053 // 1054 // // es5 (transformed) 1055 // var C = (function (_super) { 1056 // function C() { 1057 // var _this = _super.call(this, "foo") || this; 1058 // _this.x = 1; 1059 // return _this; 1060 // } 1061 // return C; 1062 // })(Base); 1063 // ``` 1064 1065 // Since the `super()` call was the first statement, we insert the `this` capturing call to 1066 // `super()` at the top of the list of `statements` (after any pre-existing custom prologues). 1067 insertCaptureThisForNode(statements, constructor, superCallExpression || createActualThis()); 1068 1069 if (!isSufficientlyCoveredByReturnStatements(constructor.body)) { 1070 statements.push(factory.createReturnStatement(factory.createUniqueName("_this", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel))); 1071 } 1072 } 1073 } 1074 else { 1075 // If a class is not derived from a base class or does not have a call to `super()`, `this` is only 1076 // captured when necessitated by an arrow function capturing the lexical `this`: 1077 // 1078 // ``` 1079 // // es2015 1080 // class C {} 1081 // 1082 // // es5 1083 // var C = (function () { 1084 // function C() { 1085 // } 1086 // return C; 1087 // })(); 1088 // ``` 1089 insertCaptureThisForNodeIfNeeded(prologue, constructor); 1090 } 1091 1092 const block = factory.createBlock( 1093 setTextRange( 1094 factory.createNodeArray( 1095 concatenate(prologue, statements) 1096 ), 1097 /*location*/ constructor.body.statements 1098 ), 1099 /*multiLine*/ true 1100 ); 1101 1102 setTextRange(block, constructor.body); 1103 1104 return block; 1105 } 1106 1107 /** 1108 * We want to try to avoid emitting a return statement in certain cases if a user already returned something. 1109 * It would generate obviously dead code, so we'll try to make things a little bit prettier 1110 * by doing a minimal check on whether some common patterns always explicitly return. 1111 */ 1112 function isSufficientlyCoveredByReturnStatements(statement: Statement): boolean { 1113 // A return statement is considered covered. 1114 if (statement.kind === SyntaxKind.ReturnStatement) { 1115 return true; 1116 } 1117 // An if-statement with two covered branches is covered. 1118 else if (statement.kind === SyntaxKind.IfStatement) { 1119 const ifStatement = statement as IfStatement; 1120 if (ifStatement.elseStatement) { 1121 return isSufficientlyCoveredByReturnStatements(ifStatement.thenStatement) && 1122 isSufficientlyCoveredByReturnStatements(ifStatement.elseStatement); 1123 } 1124 } 1125 // A block is covered if it has a last statement which is covered. 1126 else if (statement.kind === SyntaxKind.Block) { 1127 const lastStatement = lastOrUndefined((statement as Block).statements); 1128 if (lastStatement && isSufficientlyCoveredByReturnStatements(lastStatement)) { 1129 return true; 1130 } 1131 } 1132 1133 return false; 1134 } 1135 1136 function createActualThis() { 1137 return setEmitFlags(factory.createThis(), EmitFlags.NoSubstitution); 1138 } 1139 1140 function createDefaultSuperCallOrThis() { 1141 return factory.createLogicalOr( 1142 factory.createLogicalAnd( 1143 factory.createStrictInequality( 1144 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 1145 factory.createNull() 1146 ), 1147 factory.createFunctionApplyCall( 1148 factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 1149 createActualThis(), 1150 factory.createIdentifier("arguments"), 1151 ) 1152 ), 1153 createActualThis() 1154 ); 1155 } 1156 1157 /** 1158 * Visits a parameter declaration. 1159 * 1160 * @param node A ParameterDeclaration node. 1161 */ 1162 function visitParameter(node: ParameterDeclaration): ParameterDeclaration | undefined { 1163 if (node.dotDotDotToken) { 1164 // rest parameters are elided 1165 return undefined; 1166 } 1167 else if (isBindingPattern(node.name)) { 1168 // Binding patterns are converted into a generated name and are 1169 // evaluated inside the function body. 1170 return setOriginalNode( 1171 setTextRange( 1172 factory.createParameterDeclaration( 1173 /*decorators*/ undefined, 1174 /*modifiers*/ undefined, 1175 /*dotDotDotToken*/ undefined, 1176 factory.getGeneratedNameForNode(node), 1177 /*questionToken*/ undefined, 1178 /*type*/ undefined, 1179 /*initializer*/ undefined 1180 ), 1181 /*location*/ node 1182 ), 1183 /*original*/ node 1184 ); 1185 } 1186 else if (node.initializer) { 1187 // Initializers are elided 1188 return setOriginalNode( 1189 setTextRange( 1190 factory.createParameterDeclaration( 1191 /*decorators*/ undefined, 1192 /*modifiers*/ undefined, 1193 /*dotDotDotToken*/ undefined, 1194 node.name, 1195 /*questionToken*/ undefined, 1196 /*type*/ undefined, 1197 /*initializer*/ undefined 1198 ), 1199 /*location*/ node 1200 ), 1201 /*original*/ node 1202 ); 1203 } 1204 else { 1205 return node; 1206 } 1207 } 1208 1209 function hasDefaultValueOrBindingPattern(node: ParameterDeclaration) { 1210 return node.initializer !== undefined 1211 || isBindingPattern(node.name); 1212 } 1213 1214 /** 1215 * Adds statements to the body of a function-like node if it contains parameters with 1216 * binding patterns or initializers. 1217 * 1218 * @param statements The statements for the new function body. 1219 * @param node A function-like node. 1220 */ 1221 function addDefaultValueAssignmentsIfNeeded(statements: Statement[], node: FunctionLikeDeclaration): boolean { 1222 if (!some(node.parameters, hasDefaultValueOrBindingPattern)) { 1223 return false; 1224 } 1225 1226 let added = false; 1227 for (const parameter of node.parameters) { 1228 const { name, initializer, dotDotDotToken } = parameter; 1229 1230 // A rest parameter cannot have a binding pattern or an initializer, 1231 // so let's just ignore it. 1232 if (dotDotDotToken) { 1233 continue; 1234 } 1235 1236 if (isBindingPattern(name)) { 1237 added = insertDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer) || added; 1238 } 1239 else if (initializer) { 1240 insertDefaultValueAssignmentForInitializer(statements, parameter, name, initializer); 1241 added = true; 1242 } 1243 } 1244 return added; 1245 } 1246 1247 /** 1248 * Adds statements to the body of a function-like node for parameters with binding patterns 1249 * 1250 * @param statements The statements for the new function body. 1251 * @param parameter The parameter for the function. 1252 * @param name The name of the parameter. 1253 * @param initializer The initializer for the parameter. 1254 */ 1255 function insertDefaultValueAssignmentForBindingPattern(statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression | undefined): boolean { 1256 // In cases where a binding pattern is simply '[]' or '{}', 1257 // we usually don't want to emit a var declaration; however, in the presence 1258 // of an initializer, we must emit that expression to preserve side effects. 1259 if (name.elements.length > 0) { 1260 insertStatementAfterCustomPrologue( 1261 statements, 1262 setEmitFlags( 1263 factory.createVariableStatement( 1264 /*modifiers*/ undefined, 1265 factory.createVariableDeclarationList( 1266 flattenDestructuringBinding( 1267 parameter, 1268 visitor, 1269 context, 1270 FlattenLevel.All, 1271 factory.getGeneratedNameForNode(parameter) 1272 ) 1273 ) 1274 ), 1275 EmitFlags.CustomPrologue 1276 ) 1277 ); 1278 return true; 1279 } 1280 else if (initializer) { 1281 insertStatementAfterCustomPrologue( 1282 statements, 1283 setEmitFlags( 1284 factory.createExpressionStatement( 1285 factory.createAssignment( 1286 factory.getGeneratedNameForNode(parameter), 1287 visitNode(initializer, visitor, isExpression) 1288 ) 1289 ), 1290 EmitFlags.CustomPrologue 1291 ) 1292 ); 1293 return true; 1294 } 1295 return false; 1296 } 1297 1298 /** 1299 * Adds statements to the body of a function-like node for parameters with initializers. 1300 * 1301 * @param statements The statements for the new function body. 1302 * @param parameter The parameter for the function. 1303 * @param name The name of the parameter. 1304 * @param initializer The initializer for the parameter. 1305 */ 1306 function insertDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void { 1307 initializer = visitNode(initializer, visitor, isExpression); 1308 const statement = factory.createIfStatement( 1309 factory.createTypeCheck(factory.cloneNode(name), "undefined"), 1310 setEmitFlags( 1311 setTextRange( 1312 factory.createBlock([ 1313 factory.createExpressionStatement( 1314 setEmitFlags( 1315 setTextRange( 1316 factory.createAssignment( 1317 // TODO(rbuckton): Does this need to be parented? 1318 setEmitFlags(setParent(setTextRange(factory.cloneNode(name), name), name.parent), EmitFlags.NoSourceMap), 1319 setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer) | EmitFlags.NoComments) 1320 ), 1321 parameter 1322 ), 1323 EmitFlags.NoComments 1324 ) 1325 ) 1326 ]), 1327 parameter 1328 ), 1329 EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments 1330 ) 1331 ); 1332 1333 startOnNewLine(statement); 1334 setTextRange(statement, parameter); 1335 setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments); 1336 insertStatementAfterCustomPrologue(statements, statement); 1337 } 1338 1339 /** 1340 * Gets a value indicating whether we need to add statements to handle a rest parameter. 1341 * 1342 * @param node A ParameterDeclaration node. 1343 * @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is 1344 * part of a constructor declaration with a 1345 * synthesized call to `super` 1346 */ 1347 function shouldAddRestParameter(node: ParameterDeclaration | undefined, inConstructorWithSynthesizedSuper: boolean): node is ParameterDeclaration { 1348 return !!(node && node.dotDotDotToken && !inConstructorWithSynthesizedSuper); 1349 } 1350 1351 /** 1352 * Adds statements to the body of a function-like node if it contains a rest parameter. 1353 * 1354 * @param statements The statements for the new function body. 1355 * @param node A function-like node. 1356 * @param inConstructorWithSynthesizedSuper A value indicating whether the parameter is 1357 * part of a constructor declaration with a 1358 * synthesized call to `super` 1359 */ 1360 function addRestParameterIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, inConstructorWithSynthesizedSuper: boolean): boolean { 1361 const prologueStatements: Statement[] = []; 1362 const parameter = lastOrUndefined(node.parameters); 1363 if (!shouldAddRestParameter(parameter, inConstructorWithSynthesizedSuper)) { 1364 return false; 1365 } 1366 1367 // `declarationName` is the name of the local declaration for the parameter. 1368 // TODO(rbuckton): Does this need to be parented? 1369 const declarationName = parameter.name.kind === SyntaxKind.Identifier ? setParent(setTextRange(factory.cloneNode(parameter.name), parameter.name), parameter.name.parent) : factory.createTempVariable(/*recordTempVariable*/ undefined); 1370 setEmitFlags(declarationName, EmitFlags.NoSourceMap); 1371 1372 // `expressionName` is the name of the parameter used in expressions. 1373 const expressionName = parameter.name.kind === SyntaxKind.Identifier ? factory.cloneNode(parameter.name) : declarationName; 1374 const restIndex = node.parameters.length - 1; 1375 const temp = factory.createLoopVariable(); 1376 1377 // var param = []; 1378 prologueStatements.push( 1379 setEmitFlags( 1380 setTextRange( 1381 factory.createVariableStatement( 1382 /*modifiers*/ undefined, 1383 factory.createVariableDeclarationList([ 1384 factory.createVariableDeclaration( 1385 declarationName, 1386 /*exclamationToken*/ undefined, 1387 /*type*/ undefined, 1388 factory.createArrayLiteralExpression([]) 1389 ) 1390 ]) 1391 ), 1392 /*location*/ parameter 1393 ), 1394 EmitFlags.CustomPrologue 1395 ) 1396 ); 1397 1398 // for (var _i = restIndex; _i < arguments.length; _i++) { 1399 // param[_i - restIndex] = arguments[_i]; 1400 // } 1401 const forStatement = factory.createForStatement( 1402 setTextRange( 1403 factory.createVariableDeclarationList([ 1404 factory.createVariableDeclaration(temp, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createNumericLiteral(restIndex)) 1405 ]), 1406 parameter 1407 ), 1408 setTextRange( 1409 factory.createLessThan( 1410 temp, 1411 factory.createPropertyAccessExpression(factory.createIdentifier("arguments"), "length") 1412 ), 1413 parameter 1414 ), 1415 setTextRange(factory.createPostfixIncrement(temp), parameter), 1416 factory.createBlock([ 1417 startOnNewLine( 1418 setTextRange( 1419 factory.createExpressionStatement( 1420 factory.createAssignment( 1421 factory.createElementAccessExpression( 1422 expressionName, 1423 restIndex === 0 1424 ? temp 1425 : factory.createSubtract(temp, factory.createNumericLiteral(restIndex)) 1426 ), 1427 factory.createElementAccessExpression(factory.createIdentifier("arguments"), temp) 1428 ) 1429 ), 1430 /*location*/ parameter 1431 ) 1432 ) 1433 ]) 1434 ); 1435 1436 setEmitFlags(forStatement, EmitFlags.CustomPrologue); 1437 startOnNewLine(forStatement); 1438 prologueStatements.push(forStatement); 1439 1440 if (parameter.name.kind !== SyntaxKind.Identifier) { 1441 // do the actual destructuring of the rest parameter if necessary 1442 prologueStatements.push( 1443 setEmitFlags( 1444 setTextRange( 1445 factory.createVariableStatement( 1446 /*modifiers*/ undefined, 1447 factory.createVariableDeclarationList( 1448 flattenDestructuringBinding(parameter, visitor, context, FlattenLevel.All, expressionName), 1449 ) 1450 ), 1451 parameter 1452 ), 1453 EmitFlags.CustomPrologue 1454 ) 1455 ); 1456 } 1457 1458 insertStatementsAfterCustomPrologue(statements, prologueStatements); 1459 return true; 1460 } 1461 1462 /** 1463 * Adds a statement to capture the `this` of a function declaration if it is needed. 1464 * NOTE: This must be executed *after* the subtree has been visited. 1465 * 1466 * @param statements The statements for the new function body. 1467 * @param node A node. 1468 */ 1469 function insertCaptureThisForNodeIfNeeded(statements: Statement[], node: Node): boolean { 1470 if (hierarchyFacts & HierarchyFacts.CapturedLexicalThis && node.kind !== SyntaxKind.ArrowFunction) { 1471 insertCaptureThisForNode(statements, node, factory.createThis()); 1472 return true; 1473 } 1474 return false; 1475 } 1476 1477 function insertCaptureThisForNode(statements: Statement[], node: Node, initializer: Expression | undefined): void { 1478 enableSubstitutionsForCapturedThis(); 1479 const captureThisStatement = factory.createVariableStatement( 1480 /*modifiers*/ undefined, 1481 factory.createVariableDeclarationList([ 1482 factory.createVariableDeclaration( 1483 factory.createUniqueName("_this", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 1484 /*exclamationToken*/ undefined, 1485 /*type*/ undefined, 1486 initializer 1487 ) 1488 ]) 1489 ); 1490 setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue); 1491 setSourceMapRange(captureThisStatement, node); 1492 insertStatementAfterCustomPrologue(statements, captureThisStatement); 1493 } 1494 1495 function insertCaptureNewTargetIfNeeded(statements: Statement[], node: FunctionLikeDeclaration, copyOnWrite: boolean): Statement[] { 1496 if (hierarchyFacts & HierarchyFacts.NewTarget) { 1497 let newTarget: Expression; 1498 switch (node.kind) { 1499 case SyntaxKind.ArrowFunction: 1500 return statements; 1501 1502 case SyntaxKind.MethodDeclaration: 1503 case SyntaxKind.GetAccessor: 1504 case SyntaxKind.SetAccessor: 1505 // Methods and accessors cannot be constructors, so 'new.target' will 1506 // always return 'undefined'. 1507 newTarget = factory.createVoidZero(); 1508 break; 1509 1510 case SyntaxKind.Constructor: 1511 // Class constructors can only be called with `new`, so `this.constructor` 1512 // should be relatively safe to use. 1513 newTarget = factory.createPropertyAccessExpression( 1514 setEmitFlags(factory.createThis(), EmitFlags.NoSubstitution), 1515 "constructor" 1516 ); 1517 break; 1518 1519 case SyntaxKind.FunctionDeclaration: 1520 case SyntaxKind.FunctionExpression: 1521 // Functions can be called or constructed, and may have a `this` due to 1522 // being a member or when calling an imported function via `other_1.f()`. 1523 newTarget = factory.createConditionalExpression( 1524 factory.createLogicalAnd( 1525 setEmitFlags(factory.createThis(), EmitFlags.NoSubstitution), 1526 factory.createBinaryExpression( 1527 setEmitFlags(factory.createThis(), EmitFlags.NoSubstitution), 1528 SyntaxKind.InstanceOfKeyword, 1529 factory.getLocalName(node) 1530 ) 1531 ), 1532 /*questionToken*/ undefined, 1533 factory.createPropertyAccessExpression( 1534 setEmitFlags(factory.createThis(), EmitFlags.NoSubstitution), 1535 "constructor" 1536 ), 1537 /*colonToken*/ undefined, 1538 factory.createVoidZero() 1539 ); 1540 break; 1541 1542 default: 1543 return Debug.failBadSyntaxKind(node); 1544 } 1545 1546 const captureNewTargetStatement = factory.createVariableStatement( 1547 /*modifiers*/ undefined, 1548 factory.createVariableDeclarationList([ 1549 factory.createVariableDeclaration( 1550 factory.createUniqueName("_newTarget", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), 1551 /*exclamationToken*/ undefined, 1552 /*type*/ undefined, 1553 newTarget 1554 ) 1555 ]) 1556 ); 1557 1558 setEmitFlags(captureNewTargetStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue); 1559 1560 if (copyOnWrite) { 1561 statements = statements.slice(); 1562 } 1563 1564 insertStatementAfterCustomPrologue(statements, captureNewTargetStatement); 1565 } 1566 1567 return statements; 1568 } 1569 1570 /** 1571 * Adds statements to the class body function for a class to define the members of the 1572 * class. 1573 * 1574 * @param statements The statements for the class body function. 1575 * @param node The ClassExpression or ClassDeclaration node. 1576 */ 1577 function addClassMembers(statements: Statement[], node: ClassExpression | ClassDeclaration): void { 1578 for (const member of node.members) { 1579 switch (member.kind) { 1580 case SyntaxKind.SemicolonClassElement: 1581 statements.push(transformSemicolonClassElementToStatement(<SemicolonClassElement>member)); 1582 break; 1583 1584 case SyntaxKind.MethodDeclaration: 1585 statements.push(transformClassMethodDeclarationToStatement(getClassMemberPrefix(node, member), <MethodDeclaration>member, node)); 1586 break; 1587 1588 case SyntaxKind.GetAccessor: 1589 case SyntaxKind.SetAccessor: 1590 const accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member); 1591 if (member === accessors.firstAccessor) { 1592 statements.push(transformAccessorsToStatement(getClassMemberPrefix(node, member), accessors, node)); 1593 } 1594 1595 break; 1596 1597 case SyntaxKind.Constructor: 1598 // Constructors are handled in visitClassExpression/visitClassDeclaration 1599 break; 1600 1601 default: 1602 Debug.failBadSyntaxKind(member, currentSourceFile && currentSourceFile.fileName); 1603 break; 1604 } 1605 } 1606 } 1607 1608 /** 1609 * Transforms a SemicolonClassElement into a statement for a class body function. 1610 * 1611 * @param member The SemicolonClassElement node. 1612 */ 1613 function transformSemicolonClassElementToStatement(member: SemicolonClassElement) { 1614 return setTextRange(factory.createEmptyStatement(), member); 1615 } 1616 1617 /** 1618 * Transforms a MethodDeclaration into a statement for a class body function. 1619 * 1620 * @param receiver The receiver for the member. 1621 * @param member The MethodDeclaration node. 1622 */ 1623 function transformClassMethodDeclarationToStatement(receiver: LeftHandSideExpression, member: MethodDeclaration, container: Node) { 1624 const commentRange = getCommentRange(member); 1625 const sourceMapRange = getSourceMapRange(member); 1626 const memberFunction = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined, container); 1627 const propertyName = visitNode(member.name, visitor, isPropertyName); 1628 let e: Expression; 1629 if (!isPrivateIdentifier(propertyName) && context.getCompilerOptions().useDefineForClassFields) { 1630 const name = isComputedPropertyName(propertyName) ? propertyName.expression 1631 : isIdentifier(propertyName) ? factory.createStringLiteral(unescapeLeadingUnderscores(propertyName.escapedText)) 1632 : propertyName; 1633 e = factory.createObjectDefinePropertyCall(receiver, name, factory.createPropertyDescriptor({ value: memberFunction, enumerable: false, writable: true, configurable: true })); 1634 } 1635 else { 1636 const memberName = createMemberAccessForPropertyName(factory, receiver, propertyName, /*location*/ member.name); 1637 e = factory.createAssignment(memberName, memberFunction); 1638 } 1639 setEmitFlags(memberFunction, EmitFlags.NoComments); 1640 setSourceMapRange(memberFunction, sourceMapRange); 1641 const statement = setTextRange(factory.createExpressionStatement(e), /*location*/ member); 1642 1643 setOriginalNode(statement, member); 1644 setCommentRange(statement, commentRange); 1645 1646 // The location for the statement is used to emit comments only. 1647 // No source map should be emitted for this statement to align with the 1648 // old emitter. 1649 setEmitFlags(statement, EmitFlags.NoSourceMap); 1650 return statement; 1651 } 1652 1653 /** 1654 * Transforms a set of related of get/set accessors into a statement for a class body function. 1655 * 1656 * @param receiver The receiver for the member. 1657 * @param accessors The set of related get/set accessors. 1658 */ 1659 function transformAccessorsToStatement(receiver: LeftHandSideExpression, accessors: AllAccessorDeclarations, container: Node): Statement { 1660 const statement = factory.createExpressionStatement(transformAccessorsToExpression(receiver, accessors, container, /*startsOnNewLine*/ false)); 1661 // The location for the statement is used to emit source maps only. 1662 // No comments should be emitted for this statement to align with the 1663 // old emitter. 1664 setEmitFlags(statement, EmitFlags.NoComments); 1665 setSourceMapRange(statement, getSourceMapRange(accessors.firstAccessor)); 1666 return statement; 1667 } 1668 1669 /** 1670 * Transforms a set of related get/set accessors into an expression for either a class 1671 * body function or an ObjectLiteralExpression with computed properties. 1672 * 1673 * @param receiver The receiver for the member. 1674 */ 1675 function transformAccessorsToExpression(receiver: LeftHandSideExpression, { firstAccessor, getAccessor, setAccessor }: AllAccessorDeclarations, container: Node, startsOnNewLine: boolean): Expression { 1676 // To align with source maps in the old emitter, the receiver and property name 1677 // arguments are both mapped contiguously to the accessor name. 1678 // TODO(rbuckton): Does this need to be parented? 1679 const target = setParent(setTextRange(factory.cloneNode(receiver), receiver), receiver.parent); 1680 setEmitFlags(target, EmitFlags.NoComments | EmitFlags.NoTrailingSourceMap); 1681 setSourceMapRange(target, firstAccessor.name); 1682 1683 const visitedAccessorName = visitNode(firstAccessor.name, visitor, isPropertyName); 1684 if (isPrivateIdentifier(visitedAccessorName)) { 1685 return Debug.failBadSyntaxKind(visitedAccessorName, "Encountered unhandled private identifier while transforming ES2015."); 1686 } 1687 const propertyName = createExpressionForPropertyName(factory, visitedAccessorName); 1688 setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoLeadingSourceMap); 1689 setSourceMapRange(propertyName, firstAccessor.name); 1690 1691 const properties: ObjectLiteralElementLike[] = []; 1692 if (getAccessor) { 1693 const getterFunction = transformFunctionLikeToExpression(getAccessor, /*location*/ undefined, /*name*/ undefined, container); 1694 setSourceMapRange(getterFunction, getSourceMapRange(getAccessor)); 1695 setEmitFlags(getterFunction, EmitFlags.NoLeadingComments); 1696 const getter = factory.createPropertyAssignment("get", getterFunction); 1697 setCommentRange(getter, getCommentRange(getAccessor)); 1698 properties.push(getter); 1699 } 1700 1701 if (setAccessor) { 1702 const setterFunction = transformFunctionLikeToExpression(setAccessor, /*location*/ undefined, /*name*/ undefined, container); 1703 setSourceMapRange(setterFunction, getSourceMapRange(setAccessor)); 1704 setEmitFlags(setterFunction, EmitFlags.NoLeadingComments); 1705 const setter = factory.createPropertyAssignment("set", setterFunction); 1706 setCommentRange(setter, getCommentRange(setAccessor)); 1707 properties.push(setter); 1708 } 1709 1710 properties.push( 1711 factory.createPropertyAssignment("enumerable", getAccessor || setAccessor ? factory.createFalse() : factory.createTrue()), 1712 factory.createPropertyAssignment("configurable", factory.createTrue()) 1713 ); 1714 1715 const call = factory.createCallExpression( 1716 factory.createPropertyAccessExpression(factory.createIdentifier("Object"), "defineProperty"), 1717 /*typeArguments*/ undefined, 1718 [ 1719 target, 1720 propertyName, 1721 factory.createObjectLiteralExpression(properties, /*multiLine*/ true) 1722 ] 1723 ); 1724 if (startsOnNewLine) { 1725 startOnNewLine(call); 1726 } 1727 1728 return call; 1729 } 1730 1731 /** 1732 * Visits an ArrowFunction and transforms it into a FunctionExpression. 1733 * 1734 * @param node An ArrowFunction node. 1735 */ 1736 function visitArrowFunction(node: ArrowFunction) { 1737 if (node.transformFlags & TransformFlags.ContainsLexicalThis) { 1738 hierarchyFacts |= HierarchyFacts.CapturedLexicalThis; 1739 } 1740 1741 const savedConvertedLoopState = convertedLoopState; 1742 convertedLoopState = undefined; 1743 const ancestorFacts = enterSubtree(HierarchyFacts.ArrowFunctionExcludes, HierarchyFacts.ArrowFunctionIncludes); 1744 const func = factory.createFunctionExpression( 1745 /*modifiers*/ undefined, 1746 /*asteriskToken*/ undefined, 1747 /*name*/ undefined, 1748 /*typeParameters*/ undefined, 1749 visitParameterList(node.parameters, visitor, context), 1750 /*type*/ undefined, 1751 transformFunctionBody(node) 1752 ); 1753 setTextRange(func, node); 1754 setOriginalNode(func, node); 1755 setEmitFlags(func, EmitFlags.CapturesThis); 1756 1757 if (hierarchyFacts & HierarchyFacts.CapturedLexicalThis) { 1758 enableSubstitutionsForCapturedThis(); 1759 } 1760 1761 // If an arrow function contains 1762 exitSubtree(ancestorFacts, HierarchyFacts.ArrowFunctionSubtreeExcludes, HierarchyFacts.None); 1763 1764 convertedLoopState = savedConvertedLoopState; 1765 return func; 1766 } 1767 1768 /** 1769 * Visits a FunctionExpression node. 1770 * 1771 * @param node a FunctionExpression node. 1772 */ 1773 function visitFunctionExpression(node: FunctionExpression): Expression { 1774 const ancestorFacts = getEmitFlags(node) & EmitFlags.AsyncFunctionBody 1775 ? enterSubtree(HierarchyFacts.AsyncFunctionBodyExcludes, HierarchyFacts.AsyncFunctionBodyIncludes) 1776 : enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes); 1777 const savedConvertedLoopState = convertedLoopState; 1778 convertedLoopState = undefined; 1779 1780 const parameters = visitParameterList(node.parameters, visitor, context); 1781 const body = transformFunctionBody(node); 1782 const name = hierarchyFacts & HierarchyFacts.NewTarget 1783 ? factory.getLocalName(node) 1784 : node.name; 1785 1786 exitSubtree(ancestorFacts, HierarchyFacts.FunctionSubtreeExcludes, HierarchyFacts.None); 1787 convertedLoopState = savedConvertedLoopState; 1788 return factory.updateFunctionExpression( 1789 node, 1790 /*modifiers*/ undefined, 1791 node.asteriskToken, 1792 name, 1793 /*typeParameters*/ undefined, 1794 parameters, 1795 /*type*/ undefined, 1796 body 1797 ); 1798 } 1799 1800 /** 1801 * Visits a FunctionDeclaration node. 1802 * 1803 * @param node a FunctionDeclaration node. 1804 */ 1805 function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { 1806 const savedConvertedLoopState = convertedLoopState; 1807 convertedLoopState = undefined; 1808 const ancestorFacts = enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes); 1809 const parameters = visitParameterList(node.parameters, visitor, context); 1810 const body = transformFunctionBody(node); 1811 const name = hierarchyFacts & HierarchyFacts.NewTarget 1812 ? factory.getLocalName(node) 1813 : node.name; 1814 1815 exitSubtree(ancestorFacts, HierarchyFacts.FunctionSubtreeExcludes, HierarchyFacts.None); 1816 convertedLoopState = savedConvertedLoopState; 1817 return factory.updateFunctionDeclaration( 1818 node, 1819 /*decorators*/ undefined, 1820 visitNodes(node.modifiers, visitor, isModifier), 1821 node.asteriskToken, 1822 name, 1823 /*typeParameters*/ undefined, 1824 parameters, 1825 /*type*/ undefined, 1826 body 1827 ); 1828 } 1829 1830 /** 1831 * Transforms a function-like node into a FunctionExpression. 1832 * 1833 * @param node The function-like node to transform. 1834 * @param location The source-map location for the new FunctionExpression. 1835 * @param name The name of the new FunctionExpression. 1836 */ 1837 function transformFunctionLikeToExpression(node: FunctionLikeDeclaration, location: TextRange | undefined, name: Identifier | undefined, container: Node | undefined): FunctionExpression { 1838 const savedConvertedLoopState = convertedLoopState; 1839 convertedLoopState = undefined; 1840 const ancestorFacts = container && isClassLike(container) && !hasSyntacticModifier(node, ModifierFlags.Static) 1841 ? enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes | HierarchyFacts.NonStaticClassElement) 1842 : enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes); 1843 const parameters = visitParameterList(node.parameters, visitor, context); 1844 const body = transformFunctionBody(node); 1845 if (hierarchyFacts & HierarchyFacts.NewTarget && !name && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) { 1846 name = factory.getGeneratedNameForNode(node); 1847 } 1848 1849 exitSubtree(ancestorFacts, HierarchyFacts.FunctionSubtreeExcludes, HierarchyFacts.None); 1850 convertedLoopState = savedConvertedLoopState; 1851 return setOriginalNode( 1852 setTextRange( 1853 factory.createFunctionExpression( 1854 /*modifiers*/ undefined, 1855 node.asteriskToken, 1856 name, 1857 /*typeParameters*/ undefined, 1858 parameters, 1859 /*type*/ undefined, 1860 body 1861 ), 1862 location 1863 ), 1864 /*original*/ node 1865 ); 1866 } 1867 1868 /** 1869 * Transforms the body of a function-like node. 1870 * 1871 * @param node A function-like node. 1872 */ 1873 function transformFunctionBody(node: FunctionLikeDeclaration) { 1874 let multiLine = false; // indicates whether the block *must* be emitted as multiple lines 1875 let singleLine = false; // indicates whether the block *may* be emitted as a single line 1876 let statementsLocation: TextRange; 1877 let closeBraceLocation: TextRange | undefined; 1878 1879 const prologue: Statement[] = []; 1880 const statements: Statement[] = []; 1881 const body = node.body!; 1882 let statementOffset: number | undefined; 1883 1884 resumeLexicalEnvironment(); 1885 if (isBlock(body)) { 1886 // ensureUseStrict is false because no new prologue-directive should be added. 1887 // addStandardPrologue will put already-existing directives at the beginning of the target statement-array 1888 statementOffset = factory.copyStandardPrologue(body.statements, prologue, /*ensureUseStrict*/ false); 1889 statementOffset = factory.copyCustomPrologue(body.statements, statements, statementOffset, visitor, isHoistedFunction); 1890 statementOffset = factory.copyCustomPrologue(body.statements, statements, statementOffset, visitor, isHoistedVariableStatement); 1891 } 1892 1893 multiLine = addDefaultValueAssignmentsIfNeeded(statements, node) || multiLine; 1894 multiLine = addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false) || multiLine; 1895 1896 if (isBlock(body)) { 1897 // addCustomPrologue puts already-existing directives at the beginning of the target statement-array 1898 statementOffset = factory.copyCustomPrologue(body.statements, statements, statementOffset, visitor); 1899 1900 statementsLocation = body.statements; 1901 addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset)); 1902 1903 // If the original body was a multi-line block, this must be a multi-line block. 1904 if (!multiLine && body.multiLine) { 1905 multiLine = true; 1906 } 1907 } 1908 else { 1909 Debug.assert(node.kind === SyntaxKind.ArrowFunction); 1910 1911 // To align with the old emitter, we use a synthetic end position on the location 1912 // for the statement list we synthesize when we down-level an arrow function with 1913 // an expression function body. This prevents both comments and source maps from 1914 // being emitted for the end position only. 1915 statementsLocation = moveRangeEnd(body, -1); 1916 1917 const equalsGreaterThanToken = node.equalsGreaterThanToken; 1918 if (!nodeIsSynthesized(equalsGreaterThanToken) && !nodeIsSynthesized(body)) { 1919 if (rangeEndIsOnSameLineAsRangeStart(equalsGreaterThanToken, body, currentSourceFile)) { 1920 singleLine = true; 1921 } 1922 else { 1923 multiLine = true; 1924 } 1925 } 1926 1927 const expression = visitNode(body, visitor, isExpression); 1928 const returnStatement = factory.createReturnStatement(expression); 1929 setTextRange(returnStatement, body); 1930 moveSyntheticComments(returnStatement, body); 1931 setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments); 1932 statements.push(returnStatement); 1933 1934 // To align with the source map emit for the old emitter, we set a custom 1935 // source map location for the close brace. 1936 closeBraceLocation = body; 1937 } 1938 1939 factory.mergeLexicalEnvironment(prologue, endLexicalEnvironment()); 1940 insertCaptureNewTargetIfNeeded(prologue, node, /*copyOnWrite*/ false); 1941 insertCaptureThisForNodeIfNeeded(prologue, node); 1942 1943 // If we added any final generated statements, this must be a multi-line block 1944 if (some(prologue)) { 1945 multiLine = true; 1946 } 1947 1948 statements.unshift(...prologue); 1949 if (isBlock(body) && arrayIsEqualTo(statements, body.statements)) { 1950 // no changes were made, preserve the tree 1951 return body; 1952 } 1953 1954 const block = factory.createBlock(setTextRange(factory.createNodeArray(statements), statementsLocation), multiLine); 1955 setTextRange(block, node.body); 1956 if (!multiLine && singleLine) { 1957 setEmitFlags(block, EmitFlags.SingleLine); 1958 } 1959 1960 if (closeBraceLocation) { 1961 setTokenSourceMapRange(block, SyntaxKind.CloseBraceToken, closeBraceLocation); 1962 } 1963 1964 setOriginalNode(block, node.body); 1965 return block; 1966 } 1967 1968 function visitBlock(node: Block, isFunctionBody: boolean): Block { 1969 if (isFunctionBody) { 1970 // A function body is not a block scope. 1971 return visitEachChild(node, visitor, context); 1972 } 1973 const ancestorFacts = hierarchyFacts & HierarchyFacts.IterationStatement 1974 ? enterSubtree(HierarchyFacts.IterationStatementBlockExcludes, HierarchyFacts.IterationStatementBlockIncludes) 1975 : enterSubtree(HierarchyFacts.BlockExcludes, HierarchyFacts.BlockIncludes); 1976 const updated = visitEachChild(node, visitor, context); 1977 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 1978 return updated; 1979 } 1980 1981 /** 1982 * Visits an ExpressionStatement that contains a destructuring assignment. 1983 * 1984 * @param node An ExpressionStatement node. 1985 */ 1986 function visitExpressionStatement(node: ExpressionStatement): Statement { 1987 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 1988 } 1989 1990 /** 1991 * Visits a ParenthesizedExpression that may contain a destructuring assignment. 1992 * 1993 * @param node A ParenthesizedExpression node. 1994 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 1995 * expression of an `ExpressionStatement`). 1996 */ 1997 function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression { 1998 return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context); 1999 } 2000 2001 /** 2002 * Visits a BinaryExpression that contains a destructuring assignment. 2003 * 2004 * @param node A BinaryExpression node. 2005 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 2006 * expression of an `ExpressionStatement`). 2007 */ 2008 function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression { 2009 // If we are here it is because this is a destructuring assignment. 2010 if (isDestructuringAssignment(node)) { 2011 return flattenDestructuringAssignment( 2012 node, 2013 visitor, 2014 context, 2015 FlattenLevel.All, 2016 !expressionResultIsUnused); 2017 } 2018 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 2019 return factory.updateBinaryExpression( 2020 node, 2021 visitNode(node.left, visitorWithUnusedExpressionResult, isExpression), 2022 node.operatorToken, 2023 visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression) 2024 ); 2025 } 2026 return visitEachChild(node, visitor, context); 2027 } 2028 2029 /** 2030 * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the 2031 * expression of an `ExpressionStatement`). 2032 */ 2033 function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression { 2034 if (expressionResultIsUnused) { 2035 return visitEachChild(node, visitorWithUnusedExpressionResult, context); 2036 } 2037 let result: Expression[] | undefined; 2038 for (let i = 0; i < node.elements.length; i++) { 2039 const element = node.elements[i]; 2040 const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression); 2041 if (result || visited !== element) { 2042 result ||= node.elements.slice(0, i); 2043 result.push(visited); 2044 } 2045 } 2046 const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements; 2047 return factory.updateCommaListExpression(node, elements); 2048 } 2049 2050 function isVariableStatementOfTypeScriptClassWrapper(node: VariableStatement) { 2051 return node.declarationList.declarations.length === 1 2052 && !!node.declarationList.declarations[0].initializer 2053 && !!(getEmitFlags(node.declarationList.declarations[0].initializer) & EmitFlags.TypeScriptClassWrapper); 2054 } 2055 2056 function visitVariableStatement(node: VariableStatement): Statement | undefined { 2057 const ancestorFacts = enterSubtree(HierarchyFacts.None, hasSyntacticModifier(node, ModifierFlags.Export) ? HierarchyFacts.ExportedVariableStatement : HierarchyFacts.None); 2058 let updated: Statement | undefined; 2059 if (convertedLoopState && (node.declarationList.flags & NodeFlags.BlockScoped) === 0 && !isVariableStatementOfTypeScriptClassWrapper(node)) { 2060 // we are inside a converted loop - hoist variable declarations 2061 let assignments: Expression[] | undefined; 2062 for (const decl of node.declarationList.declarations) { 2063 hoistVariableDeclarationDeclaredInConvertedLoop(convertedLoopState, decl); 2064 if (decl.initializer) { 2065 let assignment: Expression; 2066 if (isBindingPattern(decl.name)) { 2067 assignment = flattenDestructuringAssignment( 2068 decl, 2069 visitor, 2070 context, 2071 FlattenLevel.All 2072 ); 2073 } 2074 else { 2075 assignment = factory.createBinaryExpression(decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression)); 2076 setTextRange(assignment, decl); 2077 } 2078 2079 assignments = append(assignments, assignment); 2080 } 2081 } 2082 if (assignments) { 2083 updated = setTextRange(factory.createExpressionStatement(factory.inlineExpressions(assignments)), node); 2084 } 2085 else { 2086 // none of declarations has initializer - the entire variable statement can be deleted 2087 updated = undefined; 2088 } 2089 } 2090 else { 2091 updated = visitEachChild(node, visitor, context); 2092 } 2093 2094 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 2095 return updated; 2096 } 2097 2098 /** 2099 * Visits a VariableDeclarationList that is block scoped (e.g. `let` or `const`). 2100 * 2101 * @param node A VariableDeclarationList node. 2102 */ 2103 function visitVariableDeclarationList(node: VariableDeclarationList): VariableDeclarationList { 2104 if (node.flags & NodeFlags.BlockScoped || node.transformFlags & TransformFlags.ContainsBindingPattern) { 2105 if (node.flags & NodeFlags.BlockScoped) { 2106 enableSubstitutionsForBlockScopedBindings(); 2107 } 2108 2109 const declarations = flatMap(node.declarations, node.flags & NodeFlags.Let 2110 ? visitVariableDeclarationInLetDeclarationList 2111 : visitVariableDeclaration); 2112 2113 const declarationList = factory.createVariableDeclarationList(declarations); 2114 setOriginalNode(declarationList, node); 2115 setTextRange(declarationList, node); 2116 setCommentRange(declarationList, node); 2117 2118 // If the first or last declaration is a binding pattern, we need to modify 2119 // the source map range for the declaration list. 2120 if (node.transformFlags & TransformFlags.ContainsBindingPattern 2121 && (isBindingPattern(node.declarations[0].name) || isBindingPattern(last(node.declarations).name))) { 2122 setSourceMapRange(declarationList, getRangeUnion(declarations)); 2123 } 2124 2125 return declarationList; 2126 } 2127 return visitEachChild(node, visitor, context); 2128 } 2129 2130 function getRangeUnion(declarations: readonly Node[]): TextRange { 2131 // declarations may not be sorted by position. 2132 // pos should be the minimum* position over all nodes (that's not -1), end should be the maximum end over all nodes. 2133 let pos = -1, end = -1; 2134 for (const node of declarations) { 2135 pos = pos === -1 ? node.pos : node.pos === -1 ? pos : Math.min(pos, node.pos); 2136 end = Math.max(end, node.end); 2137 } 2138 return createRange(pos, end); 2139 } 2140 2141 /** 2142 * Gets a value indicating whether we should emit an explicit initializer for a variable 2143 * declaration in a `let` declaration list. 2144 * 2145 * @param node A VariableDeclaration node. 2146 */ 2147 function shouldEmitExplicitInitializerForLetDeclaration(node: VariableDeclaration) { 2148 // Nested let bindings might need to be initialized explicitly to preserve 2149 // ES6 semantic: 2150 // 2151 // { let x = 1; } 2152 // { let x; } // x here should be undefined. not 1 2153 // 2154 // Top level bindings never collide with anything and thus don't require 2155 // explicit initialization. As for nested let bindings there are two cases: 2156 // 2157 // - Nested let bindings that were not renamed definitely should be 2158 // initialized explicitly: 2159 // 2160 // { let x = 1; } 2161 // { let x; if (some-condition) { x = 1}; if (x) { /*1*/ } } 2162 // 2163 // Without explicit initialization code in /*1*/ can be executed even if 2164 // some-condition is evaluated to false. 2165 // 2166 // - Renaming introduces fresh name that should not collide with any 2167 // existing names, however renamed bindings sometimes also should be 2168 // explicitly initialized. One particular case: non-captured binding 2169 // declared inside loop body (but not in loop initializer): 2170 // 2171 // let x; 2172 // for (;;) { 2173 // let x; 2174 // } 2175 // 2176 // In downlevel codegen inner 'x' will be renamed so it won't collide 2177 // with outer 'x' however it will should be reset on every iteration as 2178 // if it was declared anew. 2179 // 2180 // * Why non-captured binding? 2181 // - Because if loop contains block scoped binding captured in some 2182 // function then loop body will be rewritten to have a fresh scope 2183 // on every iteration so everything will just work. 2184 // 2185 // * Why loop initializer is excluded? 2186 // - Since we've introduced a fresh name it already will be undefined. 2187 2188 const flags = resolver.getNodeCheckFlags(node); 2189 const isCapturedInFunction = flags & NodeCheckFlags.CapturedBlockScopedBinding; 2190 const isDeclaredInLoop = flags & NodeCheckFlags.BlockScopedBindingInLoop; 2191 const emittedAsTopLevel = 2192 (hierarchyFacts & HierarchyFacts.TopLevel) !== 0 2193 || (isCapturedInFunction 2194 && isDeclaredInLoop 2195 && (hierarchyFacts & HierarchyFacts.IterationStatementBlock) !== 0); 2196 2197 const emitExplicitInitializer = 2198 !emittedAsTopLevel 2199 && (hierarchyFacts & HierarchyFacts.ForInOrForOfStatement) === 0 2200 && (!resolver.isDeclarationWithCollidingName(node) 2201 || (isDeclaredInLoop 2202 && !isCapturedInFunction 2203 && (hierarchyFacts & (HierarchyFacts.ForStatement | HierarchyFacts.ForInOrForOfStatement)) === 0)); 2204 2205 return emitExplicitInitializer; 2206 } 2207 2208 /** 2209 * Visits a VariableDeclaration in a `let` declaration list. 2210 * 2211 * @param node A VariableDeclaration node. 2212 */ 2213 function visitVariableDeclarationInLetDeclarationList(node: VariableDeclaration) { 2214 // For binding pattern names that lack initializers there is no point to emit 2215 // explicit initializer since downlevel codegen for destructuring will fail 2216 // in the absence of initializer so all binding elements will say uninitialized 2217 const name = node.name; 2218 if (isBindingPattern(name)) { 2219 return visitVariableDeclaration(node); 2220 } 2221 2222 if (!node.initializer && shouldEmitExplicitInitializerForLetDeclaration(node)) { 2223 return factory.updateVariableDeclaration(node, node.name, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createVoidZero()); 2224 } 2225 2226 return visitEachChild(node, visitor, context); 2227 } 2228 2229 /** 2230 * Visits a VariableDeclaration node with a binding pattern. 2231 * 2232 * @param node A VariableDeclaration node. 2233 */ 2234 function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> { 2235 const ancestorFacts = enterSubtree(HierarchyFacts.ExportedVariableStatement, HierarchyFacts.None); 2236 let updated: VisitResult<VariableDeclaration>; 2237 if (isBindingPattern(node.name)) { 2238 updated = flattenDestructuringBinding( 2239 node, 2240 visitor, 2241 context, 2242 FlattenLevel.All, 2243 /*value*/ undefined, 2244 (ancestorFacts & HierarchyFacts.ExportedVariableStatement) !== 0 2245 ); 2246 } 2247 else { 2248 updated = visitEachChild(node, visitor, context); 2249 } 2250 2251 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 2252 return updated; 2253 } 2254 2255 function recordLabel(node: LabeledStatement) { 2256 convertedLoopState!.labels!.set(idText(node.label), true); 2257 } 2258 2259 function resetLabel(node: LabeledStatement) { 2260 convertedLoopState!.labels!.set(idText(node.label), false); 2261 } 2262 2263 function visitLabeledStatement(node: LabeledStatement): VisitResult<Statement> { 2264 if (convertedLoopState && !convertedLoopState.labels) { 2265 convertedLoopState.labels = new Map<string, boolean>(); 2266 } 2267 const statement = unwrapInnermostStatementOfLabel(node, convertedLoopState && recordLabel); 2268 return isIterationStatement(statement, /*lookInLabeledStatements*/ false) 2269 ? visitIterationStatement(statement, /*outermostLabeledStatement*/ node) 2270 : factory.restoreEnclosingLabel(visitNode(statement, visitor, isStatement, factory.liftToBlock), node, convertedLoopState && resetLabel); 2271 } 2272 2273 function visitIterationStatement(node: IterationStatement, outermostLabeledStatement: LabeledStatement) { 2274 switch (node.kind) { 2275 case SyntaxKind.DoStatement: 2276 case SyntaxKind.WhileStatement: 2277 return visitDoOrWhileStatement(<DoStatement | WhileStatement>node, outermostLabeledStatement); 2278 case SyntaxKind.ForStatement: 2279 return visitForStatement(<ForStatement>node, outermostLabeledStatement); 2280 case SyntaxKind.ForInStatement: 2281 return visitForInStatement(<ForInStatement>node, outermostLabeledStatement); 2282 case SyntaxKind.ForOfStatement: 2283 return visitForOfStatement(<ForOfStatement>node, outermostLabeledStatement); 2284 } 2285 } 2286 2287 function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) { 2288 const ancestorFacts = enterSubtree(excludeFacts, includeFacts); 2289 const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert); 2290 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 2291 return updated; 2292 } 2293 2294 function visitDoOrWhileStatement(node: DoStatement | WhileStatement, outermostLabeledStatement: LabeledStatement | undefined) { 2295 return visitIterationStatementWithFacts( 2296 HierarchyFacts.DoOrWhileStatementExcludes, 2297 HierarchyFacts.DoOrWhileStatementIncludes, 2298 node, 2299 outermostLabeledStatement); 2300 } 2301 2302 function visitForStatement(node: ForStatement, outermostLabeledStatement: LabeledStatement | undefined) { 2303 return visitIterationStatementWithFacts( 2304 HierarchyFacts.ForStatementExcludes, 2305 HierarchyFacts.ForStatementIncludes, 2306 node, 2307 outermostLabeledStatement); 2308 } 2309 2310 function visitEachChildOfForStatement(node: ForStatement) { 2311 return factory.updateForStatement( 2312 node, 2313 visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer), 2314 visitNode(node.condition, visitor, isExpression), 2315 visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression), 2316 visitNode(node.statement, visitor, isStatement, factory.liftToBlock) 2317 ); 2318 } 2319 2320 function visitForInStatement(node: ForInStatement, outermostLabeledStatement: LabeledStatement | undefined) { 2321 return visitIterationStatementWithFacts( 2322 HierarchyFacts.ForInOrForOfStatementExcludes, 2323 HierarchyFacts.ForInOrForOfStatementIncludes, 2324 node, 2325 outermostLabeledStatement); 2326 } 2327 2328 function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> { 2329 return visitIterationStatementWithFacts( 2330 HierarchyFacts.ForInOrForOfStatementExcludes, 2331 HierarchyFacts.ForInOrForOfStatementIncludes, 2332 node, 2333 outermostLabeledStatement, 2334 compilerOptions.downlevelIteration ? convertForOfStatementForIterable : convertForOfStatementForArray); 2335 } 2336 2337 function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression, convertedLoopBodyStatements: Statement[]) { 2338 const statements: Statement[] = []; 2339 const initializer = node.initializer; 2340 if (isVariableDeclarationList(initializer)) { 2341 if (node.initializer.flags & NodeFlags.BlockScoped) { 2342 enableSubstitutionsForBlockScopedBindings(); 2343 } 2344 2345 const firstOriginalDeclaration = firstOrUndefined(initializer.declarations); 2346 if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { 2347 // This works whether the declaration is a var, let, or const. 2348 // It will use rhsIterationValue _a[_i] as the initializer. 2349 const declarations = flattenDestructuringBinding( 2350 firstOriginalDeclaration, 2351 visitor, 2352 context, 2353 FlattenLevel.All, 2354 boundValue 2355 ); 2356 2357 const declarationList = setTextRange(factory.createVariableDeclarationList(declarations), node.initializer); 2358 setOriginalNode(declarationList, node.initializer); 2359 2360 // Adjust the source map range for the first declaration to align with the old 2361 // emitter. 2362 setSourceMapRange(declarationList, createRange(declarations[0].pos, last(declarations).end)); 2363 2364 statements.push( 2365 factory.createVariableStatement( 2366 /*modifiers*/ undefined, 2367 declarationList 2368 ) 2369 ); 2370 } 2371 else { 2372 // The following call does not include the initializer, so we have 2373 // to emit it separately. 2374 statements.push( 2375 setTextRange( 2376 factory.createVariableStatement( 2377 /*modifiers*/ undefined, 2378 setOriginalNode( 2379 setTextRange( 2380 factory.createVariableDeclarationList([ 2381 factory.createVariableDeclaration( 2382 firstOriginalDeclaration ? firstOriginalDeclaration.name : factory.createTempVariable(/*recordTempVariable*/ undefined), 2383 /*exclamationToken*/ undefined, 2384 /*type*/ undefined, 2385 boundValue 2386 ) 2387 ]), 2388 moveRangePos(initializer, -1) 2389 ), 2390 initializer 2391 ) 2392 ), 2393 moveRangeEnd(initializer, -1) 2394 ) 2395 ); 2396 } 2397 } 2398 else { 2399 // Initializer is an expression. Emit the expression in the body, so that it's 2400 // evaluated on every iteration. 2401 const assignment = factory.createAssignment(initializer, boundValue); 2402 if (isDestructuringAssignment(assignment)) { 2403 statements.push(factory.createExpressionStatement(visitBinaryExpression(assignment, /*expressionResultIsUnused*/ true))); 2404 } 2405 else { 2406 setTextRangeEnd(assignment, initializer.end); 2407 statements.push(setTextRange(factory.createExpressionStatement(visitNode(assignment, visitor, isExpression)), moveRangeEnd(initializer, -1))); 2408 } 2409 } 2410 2411 if (convertedLoopBodyStatements) { 2412 return createSyntheticBlockForConvertedStatements(addRange(statements, convertedLoopBodyStatements)); 2413 } 2414 else { 2415 const statement = visitNode(node.statement, visitor, isStatement, factory.liftToBlock); 2416 if (isBlock(statement)) { 2417 return factory.updateBlock(statement, setTextRange(factory.createNodeArray(concatenate(statements, statement.statements)), statement.statements)); 2418 } 2419 else { 2420 statements.push(statement); 2421 return createSyntheticBlockForConvertedStatements(statements); 2422 } 2423 } 2424 } 2425 2426 function createSyntheticBlockForConvertedStatements(statements: Statement[]) { 2427 return setEmitFlags( 2428 factory.createBlock( 2429 factory.createNodeArray(statements), 2430 /*multiLine*/ true 2431 ), 2432 EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps 2433 ); 2434 } 2435 2436 function convertForOfStatementForArray(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement { 2437 // The following ES6 code: 2438 // 2439 // for (let v of expr) { } 2440 // 2441 // should be emitted as 2442 // 2443 // for (var _i = 0, _a = expr; _i < _a.length; _i++) { 2444 // var v = _a[_i]; 2445 // } 2446 // 2447 // where _a and _i are temps emitted to capture the RHS and the counter, 2448 // respectively. 2449 // When the left hand side is an expression instead of a let declaration, 2450 // the "let v" is not emitted. 2451 // When the left hand side is a let/const, the v is renamed if there is 2452 // another v in scope. 2453 // Note that all assignments to the LHS are emitted in the body, including 2454 // all destructuring. 2455 // Note also that because an extra statement is needed to assign to the LHS, 2456 // for-of bodies are always emitted as blocks. 2457 2458 const expression = visitNode(node.expression, visitor, isExpression); 2459 2460 // In the case where the user wrote an identifier as the RHS, like this: 2461 // 2462 // for (let v of arr) { } 2463 // 2464 // we don't want to emit a temporary variable for the RHS, just use it directly. 2465 const counter = factory.createLoopVariable(); 2466 const rhsReference = isIdentifier(expression) ? factory.getGeneratedNameForNode(expression) : factory.createTempVariable(/*recordTempVariable*/ undefined); 2467 2468 // The old emitter does not emit source maps for the expression 2469 setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression)); 2470 2471 const forStatement = setTextRange( 2472 factory.createForStatement( 2473 /*initializer*/ setEmitFlags( 2474 setTextRange( 2475 factory.createVariableDeclarationList([ 2476 setTextRange(factory.createVariableDeclaration(counter, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createNumericLiteral(0)), moveRangePos(node.expression, -1)), 2477 setTextRange(factory.createVariableDeclaration(rhsReference, /*exclamationToken*/ undefined, /*type*/ undefined, expression), node.expression) 2478 ]), 2479 node.expression 2480 ), 2481 EmitFlags.NoHoisting 2482 ), 2483 /*condition*/ setTextRange( 2484 factory.createLessThan( 2485 counter, 2486 factory.createPropertyAccessExpression(rhsReference, "length") 2487 ), 2488 node.expression 2489 ), 2490 /*incrementor*/ setTextRange(factory.createPostfixIncrement(counter), node.expression), 2491 /*statement*/ convertForOfStatementHead( 2492 node, 2493 factory.createElementAccessExpression(rhsReference, counter), 2494 convertedLoopBodyStatements 2495 ) 2496 ), 2497 /*location*/ node 2498 ); 2499 2500 // Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter. 2501 setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps); 2502 setTextRange(forStatement, node); 2503 return factory.restoreEnclosingLabel(forStatement, outermostLabeledStatement, convertedLoopState && resetLabel); 2504 } 2505 2506 function convertForOfStatementForIterable(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], ancestorFacts: HierarchyFacts): Statement { 2507 const expression = visitNode(node.expression, visitor, isExpression); 2508 const iterator = isIdentifier(expression) ? factory.getGeneratedNameForNode(expression) : factory.createTempVariable(/*recordTempVariable*/ undefined); 2509 const result = isIdentifier(expression) ? factory.getGeneratedNameForNode(iterator) : factory.createTempVariable(/*recordTempVariable*/ undefined); 2510 const errorRecord = factory.createUniqueName("e"); 2511 const catchVariable = factory.getGeneratedNameForNode(errorRecord); 2512 const returnMethod = factory.createTempVariable(/*recordTempVariable*/ undefined); 2513 const values = setTextRange(emitHelpers().createValuesHelper(expression), node.expression); 2514 const next = factory.createCallExpression(factory.createPropertyAccessExpression(iterator, "next"), /*typeArguments*/ undefined, []); 2515 2516 hoistVariableDeclaration(errorRecord); 2517 hoistVariableDeclaration(returnMethod); 2518 2519 // if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration 2520 const initializer = ancestorFacts & HierarchyFacts.IterationContainer 2521 ? factory.inlineExpressions([factory.createAssignment(errorRecord, factory.createVoidZero()), values]) 2522 : values; 2523 2524 const forStatement = setEmitFlags( 2525 setTextRange( 2526 factory.createForStatement( 2527 /*initializer*/ setEmitFlags( 2528 setTextRange( 2529 factory.createVariableDeclarationList([ 2530 setTextRange(factory.createVariableDeclaration(iterator, /*exclamationToken*/ undefined, /*type*/ undefined, initializer), node.expression), 2531 factory.createVariableDeclaration(result, /*exclamationToken*/ undefined, /*type*/ undefined, next) 2532 ]), 2533 node.expression 2534 ), 2535 EmitFlags.NoHoisting 2536 ), 2537 /*condition*/ factory.createLogicalNot(factory.createPropertyAccessExpression(result, "done")), 2538 /*incrementor*/ factory.createAssignment(result, next), 2539 /*statement*/ convertForOfStatementHead( 2540 node, 2541 factory.createPropertyAccessExpression(result, "value"), 2542 convertedLoopBodyStatements 2543 ) 2544 ), 2545 /*location*/ node 2546 ), 2547 EmitFlags.NoTokenTrailingSourceMaps 2548 ); 2549 2550 return factory.createTryStatement( 2551 factory.createBlock([ 2552 factory.restoreEnclosingLabel( 2553 forStatement, 2554 outermostLabeledStatement, 2555 convertedLoopState && resetLabel 2556 ) 2557 ]), 2558 factory.createCatchClause(factory.createVariableDeclaration(catchVariable), 2559 setEmitFlags( 2560 factory.createBlock([ 2561 factory.createExpressionStatement( 2562 factory.createAssignment( 2563 errorRecord, 2564 factory.createObjectLiteralExpression([ 2565 factory.createPropertyAssignment("error", catchVariable) 2566 ]) 2567 ) 2568 ) 2569 ]), 2570 EmitFlags.SingleLine 2571 ) 2572 ), 2573 factory.createBlock([ 2574 factory.createTryStatement( 2575 /*tryBlock*/ factory.createBlock([ 2576 setEmitFlags( 2577 factory.createIfStatement( 2578 factory.createLogicalAnd( 2579 factory.createLogicalAnd( 2580 result, 2581 factory.createLogicalNot( 2582 factory.createPropertyAccessExpression(result, "done") 2583 ) 2584 ), 2585 factory.createAssignment( 2586 returnMethod, 2587 factory.createPropertyAccessExpression(iterator, "return") 2588 ) 2589 ), 2590 factory.createExpressionStatement( 2591 factory.createFunctionCallCall(returnMethod, iterator, []) 2592 ) 2593 ), 2594 EmitFlags.SingleLine 2595 ), 2596 ]), 2597 /*catchClause*/ undefined, 2598 /*finallyBlock*/ setEmitFlags( 2599 factory.createBlock([ 2600 setEmitFlags( 2601 factory.createIfStatement( 2602 errorRecord, 2603 factory.createThrowStatement( 2604 factory.createPropertyAccessExpression(errorRecord, "error") 2605 ) 2606 ), 2607 EmitFlags.SingleLine 2608 ) 2609 ]), 2610 EmitFlags.SingleLine 2611 ) 2612 ) 2613 ]) 2614 ); 2615 } 2616 2617 /** 2618 * Visits an ObjectLiteralExpression with computed property names. 2619 * 2620 * @param node An ObjectLiteralExpression node. 2621 */ 2622 function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { 2623 const properties = node.properties; 2624 2625 // Find the first computed property. 2626 // Everything until that point can be emitted as part of the initial object literal. 2627 let numInitialProperties = -1, hasComputed = false; 2628 for (let i = 0; i < properties.length; i++) { 2629 const property = properties[i]; 2630 if ((property.transformFlags & TransformFlags.ContainsYield && 2631 hierarchyFacts & HierarchyFacts.AsyncFunctionBody) 2632 || (hasComputed = Debug.checkDefined(property.name).kind === SyntaxKind.ComputedPropertyName)) { 2633 numInitialProperties = i; 2634 break; 2635 } 2636 } 2637 2638 if (numInitialProperties < 0) { 2639 return visitEachChild(node, visitor, context); 2640 } 2641 2642 // For computed properties, we need to create a unique handle to the object 2643 // literal so we can modify it without risking internal assignments tainting the object. 2644 const temp = factory.createTempVariable(hoistVariableDeclaration); 2645 2646 // Write out the first non-computed properties, then emit the rest through indexing on the temp variable. 2647 const expressions: Expression[] = []; 2648 const assignment = factory.createAssignment( 2649 temp, 2650 setEmitFlags( 2651 factory.createObjectLiteralExpression( 2652 visitNodes(properties, visitor, isObjectLiteralElementLike, 0, numInitialProperties), 2653 node.multiLine 2654 ), 2655 hasComputed ? EmitFlags.Indented : 0 2656 ) 2657 ); 2658 2659 if (node.multiLine) { 2660 startOnNewLine(assignment); 2661 } 2662 2663 expressions.push(assignment); 2664 2665 addObjectLiteralMembers(expressions, node, temp, numInitialProperties); 2666 2667 // We need to clone the temporary identifier so that we can write it on a 2668 // new line 2669 expressions.push(node.multiLine ? startOnNewLine(setParent(setTextRange(factory.cloneNode(temp), temp), temp.parent)) : temp); 2670 return factory.inlineExpressions(expressions); 2671 } 2672 2673 interface ForStatementWithConvertibleInitializer extends ForStatement { 2674 initializer: VariableDeclarationList; 2675 } 2676 2677 interface ForStatementWithConvertibleCondition extends ForStatement { 2678 condition: Expression; 2679 } 2680 2681 interface ForStatementWithConvertibleIncrementor extends ForStatement { 2682 incrementor: Expression; 2683 } 2684 2685 function shouldConvertPartOfIterationStatement(node: Node) { 2686 return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ContainsCapturedBlockScopeBinding) !== 0; 2687 } 2688 2689 function shouldConvertInitializerOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleInitializer { 2690 return isForStatement(node) && !!node.initializer && shouldConvertPartOfIterationStatement(node.initializer); 2691 } 2692 2693 function shouldConvertConditionOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleCondition { 2694 return isForStatement(node) && !!node.condition && shouldConvertPartOfIterationStatement(node.condition); 2695 } 2696 2697 function shouldConvertIncrementorOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleIncrementor { 2698 return isForStatement(node) && !!node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor); 2699 } 2700 2701 function shouldConvertIterationStatement(node: IterationStatement) { 2702 return shouldConvertBodyOfIterationStatement(node) 2703 || shouldConvertInitializerOfForStatement(node); 2704 } 2705 2706 function shouldConvertBodyOfIterationStatement(node: IterationStatement): boolean { 2707 return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0; 2708 } 2709 2710 /** 2711 * Records constituents of name for the given variable to be hoisted in the outer scope. 2712 */ 2713 function hoistVariableDeclarationDeclaredInConvertedLoop(state: ConvertedLoopState, node: VariableDeclaration): void { 2714 if (!state.hoistedLocalVariables) { 2715 state.hoistedLocalVariables = []; 2716 } 2717 2718 visit(node.name); 2719 2720 function visit(node: Identifier | BindingPattern) { 2721 if (node.kind === SyntaxKind.Identifier) { 2722 state.hoistedLocalVariables!.push(node); 2723 } 2724 else { 2725 for (const element of node.elements) { 2726 if (!isOmittedExpression(element)) { 2727 visit(element.name); 2728 } 2729 } 2730 } 2731 } 2732 } 2733 2734 function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> { 2735 if (!shouldConvertIterationStatement(node)) { 2736 let saveAllowedNonLabeledJumps: Jump | undefined; 2737 if (convertedLoopState) { 2738 // we get here if we are trying to emit normal loop loop inside converted loop 2739 // set allowedNonLabeledJumps to Break | Continue to mark that break\continue inside the loop should be emitted as is 2740 saveAllowedNonLabeledJumps = convertedLoopState.allowedNonLabeledJumps; 2741 convertedLoopState.allowedNonLabeledJumps = Jump.Break | Jump.Continue; 2742 } 2743 2744 const result = convert 2745 ? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts) 2746 : factory.restoreEnclosingLabel( 2747 isForStatement(node) ? visitEachChildOfForStatement(node) : visitEachChild(node, visitor, context), 2748 outermostLabeledStatement, 2749 convertedLoopState && resetLabel); 2750 2751 if (convertedLoopState) { 2752 convertedLoopState.allowedNonLabeledJumps = saveAllowedNonLabeledJumps; 2753 } 2754 return result; 2755 } 2756 2757 const currentState = createConvertedLoopState(node); 2758 const statements: Statement[] = []; 2759 2760 const outerConvertedLoopState = convertedLoopState; 2761 convertedLoopState = currentState; 2762 2763 const initializerFunction = shouldConvertInitializerOfForStatement(node) ? createFunctionForInitializerOfForStatement(node, currentState) : undefined; 2764 const bodyFunction = shouldConvertBodyOfIterationStatement(node) ? createFunctionForBodyOfIterationStatement(node, currentState, outerConvertedLoopState) : undefined; 2765 2766 convertedLoopState = outerConvertedLoopState; 2767 2768 if (initializerFunction) statements.push(initializerFunction.functionDeclaration); 2769 if (bodyFunction) statements.push(bodyFunction.functionDeclaration); 2770 2771 addExtraDeclarationsForConvertedLoop(statements, currentState, outerConvertedLoopState); 2772 2773 if (initializerFunction) { 2774 statements.push(generateCallToConvertedLoopInitializer(initializerFunction.functionName, initializerFunction.containsYield)); 2775 } 2776 2777 let loop: Statement; 2778 if (bodyFunction) { 2779 if (convert) { 2780 loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts); 2781 } 2782 else { 2783 const clone = convertIterationStatementCore(node, initializerFunction, factory.createBlock(bodyFunction.part, /*multiLine*/ true)); 2784 loop = factory.restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel); 2785 } 2786 } 2787 else { 2788 const clone = convertIterationStatementCore(node, initializerFunction, visitNode(node.statement, visitor, isStatement, factory.liftToBlock)); 2789 loop = factory.restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel); 2790 } 2791 2792 statements.push(loop); 2793 return statements; 2794 } 2795 2796 function convertIterationStatementCore(node: IterationStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) { 2797 switch (node.kind) { 2798 case SyntaxKind.ForStatement: return convertForStatement(node as ForStatement, initializerFunction, convertedLoopBody); 2799 case SyntaxKind.ForInStatement: return convertForInStatement(node as ForInStatement, convertedLoopBody); 2800 case SyntaxKind.ForOfStatement: return convertForOfStatement(node as ForOfStatement, convertedLoopBody); 2801 case SyntaxKind.DoStatement: return convertDoStatement(node as DoStatement, convertedLoopBody); 2802 case SyntaxKind.WhileStatement: return convertWhileStatement(node as WhileStatement, convertedLoopBody); 2803 default: return Debug.failBadSyntaxKind(node, "IterationStatement expected"); 2804 } 2805 } 2806 2807 function convertForStatement(node: ForStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) { 2808 const shouldConvertCondition = node.condition && shouldConvertPartOfIterationStatement(node.condition); 2809 const shouldConvertIncrementor = shouldConvertCondition || node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor); 2810 return factory.updateForStatement( 2811 node, 2812 visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitorWithUnusedExpressionResult, isForInitializer), 2813 visitNode(shouldConvertCondition ? undefined : node.condition, visitor, isExpression), 2814 visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitorWithUnusedExpressionResult, isExpression), 2815 convertedLoopBody 2816 ); 2817 } 2818 2819 function convertForOfStatement(node: ForOfStatement, convertedLoopBody: Statement) { 2820 return factory.updateForOfStatement( 2821 node, 2822 /*awaitModifier*/ undefined, 2823 visitNode(node.initializer, visitor, isForInitializer), 2824 visitNode(node.expression, visitor, isExpression), 2825 convertedLoopBody); 2826 } 2827 2828 function convertForInStatement(node: ForInStatement, convertedLoopBody: Statement) { 2829 return factory.updateForInStatement( 2830 node, 2831 visitNode(node.initializer, visitor, isForInitializer), 2832 visitNode(node.expression, visitor, isExpression), 2833 convertedLoopBody); 2834 } 2835 2836 function convertDoStatement(node: DoStatement, convertedLoopBody: Statement) { 2837 return factory.updateDoStatement( 2838 node, 2839 convertedLoopBody, 2840 visitNode(node.expression, visitor, isExpression)); 2841 } 2842 2843 function convertWhileStatement(node: WhileStatement, convertedLoopBody: Statement) { 2844 return factory.updateWhileStatement( 2845 node, 2846 visitNode(node.expression, visitor, isExpression), 2847 convertedLoopBody); 2848 } 2849 2850 function createConvertedLoopState(node: IterationStatement) { 2851 let loopInitializer: VariableDeclarationList | undefined; 2852 switch (node.kind) { 2853 case SyntaxKind.ForStatement: 2854 case SyntaxKind.ForInStatement: 2855 case SyntaxKind.ForOfStatement: 2856 const initializer = (<ForStatement | ForInStatement | ForOfStatement>node).initializer; 2857 if (initializer && initializer.kind === SyntaxKind.VariableDeclarationList) { 2858 loopInitializer = <VariableDeclarationList>initializer; 2859 } 2860 break; 2861 } 2862 2863 // variables that will be passed to the loop as parameters 2864 const loopParameters: ParameterDeclaration[] = []; 2865 // variables declared in the loop initializer that will be changed inside the loop 2866 const loopOutParameters: LoopOutParameter[] = []; 2867 if (loopInitializer && (getCombinedNodeFlags(loopInitializer) & NodeFlags.BlockScoped)) { 2868 const hasCapturedBindingsInForInitializer = shouldConvertInitializerOfForStatement(node); 2869 for (const decl of loopInitializer.declarations) { 2870 processLoopVariableDeclaration(node, decl, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer); 2871 } 2872 } 2873 2874 const currentState: ConvertedLoopState = { loopParameters, loopOutParameters }; 2875 if (convertedLoopState) { 2876 // convertedOuterLoopState !== undefined means that this converted loop is nested in another converted loop. 2877 // if outer converted loop has already accumulated some state - pass it through 2878 if (convertedLoopState.argumentsName) { 2879 // outer loop has already used 'arguments' so we've already have some name to alias it 2880 // use the same name in all nested loops 2881 currentState.argumentsName = convertedLoopState.argumentsName; 2882 } 2883 if (convertedLoopState.thisName) { 2884 // outer loop has already used 'this' so we've already have some name to alias it 2885 // use the same name in all nested loops 2886 currentState.thisName = convertedLoopState.thisName; 2887 } 2888 if (convertedLoopState.hoistedLocalVariables) { 2889 // we've already collected some non-block scoped variable declarations in enclosing loop 2890 // use the same storage in nested loop 2891 currentState.hoistedLocalVariables = convertedLoopState.hoistedLocalVariables; 2892 } 2893 } 2894 return currentState; 2895 } 2896 2897 function addExtraDeclarationsForConvertedLoop(statements: Statement[], state: ConvertedLoopState, outerState: ConvertedLoopState | undefined) { 2898 let extraVariableDeclarations: VariableDeclaration[] | undefined; 2899 // propagate state from the inner loop to the outer loop if necessary 2900 if (state.argumentsName) { 2901 // if alias for arguments is set 2902 if (outerState) { 2903 // pass it to outer converted loop 2904 outerState.argumentsName = state.argumentsName; 2905 } 2906 else { 2907 // this is top level converted loop and we need to create an alias for 'arguments' object 2908 (extraVariableDeclarations || (extraVariableDeclarations = [])).push( 2909 factory.createVariableDeclaration( 2910 state.argumentsName, 2911 /*exclamationToken*/ undefined, 2912 /*type*/ undefined, 2913 factory.createIdentifier("arguments") 2914 ) 2915 ); 2916 } 2917 } 2918 2919 if (state.thisName) { 2920 // if alias for this is set 2921 if (outerState) { 2922 // pass it to outer converted loop 2923 outerState.thisName = state.thisName; 2924 } 2925 else { 2926 // this is top level converted loop so we need to create an alias for 'this' here 2927 // NOTE: 2928 // if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set. 2929 // If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'. 2930 (extraVariableDeclarations || (extraVariableDeclarations = [])).push( 2931 factory.createVariableDeclaration( 2932 state.thisName, 2933 /*exclamationToken*/ undefined, 2934 /*type*/ undefined, 2935 factory.createIdentifier("this") 2936 ) 2937 ); 2938 } 2939 } 2940 2941 if (state.hoistedLocalVariables) { 2942 // if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later 2943 if (outerState) { 2944 // pass them to outer converted loop 2945 outerState.hoistedLocalVariables = state.hoistedLocalVariables; 2946 } 2947 else { 2948 if (!extraVariableDeclarations) { 2949 extraVariableDeclarations = []; 2950 } 2951 // hoist collected variable declarations 2952 for (const identifier of state.hoistedLocalVariables) { 2953 extraVariableDeclarations.push(factory.createVariableDeclaration(identifier)); 2954 } 2955 } 2956 } 2957 2958 // add extra variables to hold out parameters if necessary 2959 if (state.loopOutParameters.length) { 2960 if (!extraVariableDeclarations) { 2961 extraVariableDeclarations = []; 2962 } 2963 for (const outParam of state.loopOutParameters) { 2964 extraVariableDeclarations.push(factory.createVariableDeclaration(outParam.outParamName)); 2965 } 2966 } 2967 2968 if (state.conditionVariable) { 2969 if (!extraVariableDeclarations) { 2970 extraVariableDeclarations = []; 2971 } 2972 extraVariableDeclarations.push(factory.createVariableDeclaration(state.conditionVariable, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createFalse())); 2973 } 2974 2975 // create variable statement to hold all introduced variable declarations 2976 if (extraVariableDeclarations) { 2977 statements.push(factory.createVariableStatement( 2978 /*modifiers*/ undefined, 2979 factory.createVariableDeclarationList(extraVariableDeclarations) 2980 )); 2981 } 2982 } 2983 2984 interface IterationStatementPartFunction<T> { 2985 functionName: Identifier; 2986 functionDeclaration: Statement; 2987 containsYield: boolean; 2988 part: T; 2989 } 2990 2991 function createOutVariable(p: LoopOutParameter) { 2992 return factory.createVariableDeclaration(p.originalName, /*exclamationToken*/ undefined, /*type*/ undefined, p.outParamName); 2993 } 2994 2995 /** 2996 * Creates a `_loop_init` function for a `ForStatement` with a block-scoped initializer 2997 * that is captured in a closure inside of the initializer. The `_loop_init` function is 2998 * used to preserve the per-iteration environment semantics of 2999 * [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation). 3000 */ 3001 function createFunctionForInitializerOfForStatement(node: ForStatementWithConvertibleInitializer, currentState: ConvertedLoopState): IterationStatementPartFunction<VariableDeclarationList> { 3002 const functionName = factory.createUniqueName("_loop_init"); 3003 3004 const containsYield = (node.initializer.transformFlags & TransformFlags.ContainsYield) !== 0; 3005 let emitFlags = EmitFlags.None; 3006 if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis; 3007 if (containsYield && hierarchyFacts & HierarchyFacts.AsyncFunctionBody) emitFlags |= EmitFlags.AsyncFunctionBody; 3008 3009 const statements: Statement[] = []; 3010 statements.push(factory.createVariableStatement(/*modifiers*/ undefined, node.initializer)); 3011 copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Initializer, CopyDirection.ToOutParameter, statements); 3012 3013 // This transforms the following ES2015 syntax: 3014 // 3015 // for (let i = (setImmediate(() => console.log(i)), 0); i < 2; i++) { 3016 // // loop body 3017 // } 3018 // 3019 // Into the following ES5 syntax: 3020 // 3021 // var _loop_init_1 = function () { 3022 // var i = (setImmediate(() => console.log(i)), 0); 3023 // out_i_1 = i; 3024 // }; 3025 // var out_i_1; 3026 // _loop_init_1(); 3027 // for (var i = out_i_1; i < 2; i++) { 3028 // // loop body 3029 // } 3030 // 3031 // Which prevents mutations to `i` in the per-iteration environment of the body 3032 // from affecting the initial value for `i` outside of the per-iteration environment. 3033 3034 const functionDeclaration = factory.createVariableStatement( 3035 /*modifiers*/ undefined, 3036 setEmitFlags( 3037 factory.createVariableDeclarationList([ 3038 factory.createVariableDeclaration( 3039 functionName, 3040 /*exclamationToken*/ undefined, 3041 /*type*/ undefined, 3042 setEmitFlags( 3043 factory.createFunctionExpression( 3044 /*modifiers*/ undefined, 3045 containsYield ? factory.createToken(SyntaxKind.AsteriskToken) : undefined, 3046 /*name*/ undefined, 3047 /*typeParameters*/ undefined, 3048 /*parameters*/ undefined, 3049 /*type*/ undefined, 3050 visitNode( 3051 factory.createBlock(statements, /*multiLine*/ true), 3052 visitor, 3053 isBlock 3054 ) 3055 ), 3056 emitFlags 3057 ) 3058 ) 3059 ]), 3060 EmitFlags.NoHoisting 3061 ) 3062 ); 3063 3064 const part = factory.createVariableDeclarationList(map(currentState.loopOutParameters, createOutVariable)); 3065 return { functionName, containsYield, functionDeclaration, part }; 3066 } 3067 3068 /** 3069 * Creates a `_loop` function for an `IterationStatement` with a block-scoped initializer 3070 * that is captured in a closure inside of the loop body. The `_loop` function is used to 3071 * preserve the per-iteration environment semantics of 3072 * [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation). 3073 */ 3074 function createFunctionForBodyOfIterationStatement(node: IterationStatement, currentState: ConvertedLoopState, outerState: ConvertedLoopState | undefined): IterationStatementPartFunction<Statement[]> { 3075 const functionName = factory.createUniqueName("_loop"); 3076 startLexicalEnvironment(); 3077 const statement = visitNode(node.statement, visitor, isStatement, factory.liftToBlock); 3078 const lexicalEnvironment = endLexicalEnvironment(); 3079 3080 const statements: Statement[] = []; 3081 if (shouldConvertConditionOfForStatement(node) || shouldConvertIncrementorOfForStatement(node)) { 3082 // If a block-scoped variable declared in the initializer of `node` is captured in 3083 // the condition or incrementor, we must move the condition and incrementor into 3084 // the body of the for loop. 3085 // 3086 // This transforms the following ES2015 syntax: 3087 // 3088 // for (let i = 0; setImmediate(() => console.log(i)), i < 2; setImmediate(() => console.log(i)), i++) { 3089 // // loop body 3090 // } 3091 // 3092 // Into the following ES5 syntax: 3093 // 3094 // var _loop_1 = function (i) { 3095 // if (inc_1) 3096 // setImmediate(() => console.log(i)), i++; 3097 // else 3098 // inc_1 = true; 3099 // if (!(setImmediate(() => console.log(i)), i < 2)) 3100 // return out_i_1 = i, "break"; 3101 // // loop body 3102 // out_i_1 = i; 3103 // } 3104 // var out_i_1, inc_1 = false; 3105 // for (var i = 0;;) { 3106 // var state_1 = _loop_1(i); 3107 // i = out_i_1; 3108 // if (state_1 === "break") 3109 // break; 3110 // } 3111 // 3112 // Which prevents mutations to `i` in the per-iteration environment of the body 3113 // from affecting the value of `i` in the previous per-iteration environment. 3114 // 3115 // Note that the incrementor of a `for` loop is evaluated in a *new* per-iteration 3116 // environment that is carried over to the next iteration of the loop. As a result, 3117 // we must indicate whether this is the first evaluation of the loop body so that 3118 // we only evaluate the incrementor on subsequent evaluations. 3119 3120 currentState.conditionVariable = factory.createUniqueName("inc"); 3121 if (node.incrementor) { 3122 statements.push(factory.createIfStatement( 3123 currentState.conditionVariable, 3124 factory.createExpressionStatement(visitNode(node.incrementor, visitor, isExpression)), 3125 factory.createExpressionStatement(factory.createAssignment(currentState.conditionVariable, factory.createTrue())) 3126 )); 3127 } 3128 else { 3129 statements.push(factory.createIfStatement( 3130 factory.createLogicalNot(currentState.conditionVariable), 3131 factory.createExpressionStatement(factory.createAssignment(currentState.conditionVariable, factory.createTrue())) 3132 )); 3133 } 3134 3135 if (shouldConvertConditionOfForStatement(node)) { 3136 statements.push(factory.createIfStatement( 3137 factory.createPrefixUnaryExpression(SyntaxKind.ExclamationToken, visitNode(node.condition, visitor, isExpression)), 3138 visitNode(factory.createBreakStatement(), visitor, isStatement) 3139 )); 3140 } 3141 } 3142 3143 if (isBlock(statement)) { 3144 addRange(statements, statement.statements); 3145 } 3146 else { 3147 statements.push(statement); 3148 } 3149 3150 copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOutParameter, statements); 3151 insertStatementsAfterStandardPrologue(statements, lexicalEnvironment); 3152 3153 const loopBody = factory.createBlock(statements, /*multiLine*/ true); 3154 if (isBlock(statement)) setOriginalNode(loopBody, statement); 3155 3156 const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0; 3157 3158 let emitFlags: EmitFlags = EmitFlags.ReuseTempVariableScope; 3159 if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis; 3160 if (containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0) emitFlags |= EmitFlags.AsyncFunctionBody; 3161 3162 // This transforms the following ES2015 syntax (in addition to other variations): 3163 // 3164 // for (let i = 0; i < 2; i++) { 3165 // setImmediate(() => console.log(i)); 3166 // } 3167 // 3168 // Into the following ES5 syntax: 3169 // 3170 // var _loop_1 = function (i) { 3171 // setImmediate(() => console.log(i)); 3172 // }; 3173 // for (var i = 0; i < 2; i++) { 3174 // _loop_1(i); 3175 // } 3176 3177 const functionDeclaration = 3178 factory.createVariableStatement( 3179 /*modifiers*/ undefined, 3180 setEmitFlags( 3181 factory.createVariableDeclarationList( 3182 [ 3183 factory.createVariableDeclaration( 3184 functionName, 3185 /*exclamationToken*/ undefined, 3186 /*type*/ undefined, 3187 setEmitFlags( 3188 factory.createFunctionExpression( 3189 /*modifiers*/ undefined, 3190 containsYield ? factory.createToken(SyntaxKind.AsteriskToken) : undefined, 3191 /*name*/ undefined, 3192 /*typeParameters*/ undefined, 3193 currentState.loopParameters, 3194 /*type*/ undefined, 3195 loopBody 3196 ), 3197 emitFlags 3198 ) 3199 ) 3200 ] 3201 ), 3202 EmitFlags.NoHoisting 3203 ) 3204 ); 3205 3206 const part = generateCallToConvertedLoop(functionName, currentState, outerState, containsYield); 3207 return { functionName, containsYield, functionDeclaration, part }; 3208 } 3209 3210 function copyOutParameter(outParam: LoopOutParameter, copyDirection: CopyDirection): BinaryExpression { 3211 const source = copyDirection === CopyDirection.ToOriginal ? outParam.outParamName : outParam.originalName; 3212 const target = copyDirection === CopyDirection.ToOriginal ? outParam.originalName : outParam.outParamName; 3213 return factory.createBinaryExpression(target, SyntaxKind.EqualsToken, source); 3214 } 3215 3216 function copyOutParameters(outParams: LoopOutParameter[], partFlags: LoopOutParameterFlags, copyDirection: CopyDirection, statements: Statement[]): void { 3217 for (const outParam of outParams) { 3218 if (outParam.flags & partFlags) { 3219 statements.push(factory.createExpressionStatement(copyOutParameter(outParam, copyDirection))); 3220 } 3221 } 3222 } 3223 3224 function generateCallToConvertedLoopInitializer(initFunctionExpressionName: Identifier, containsYield: boolean): Statement { 3225 const call = factory.createCallExpression(initFunctionExpressionName, /*typeArguments*/ undefined, []); 3226 const callResult = containsYield 3227 ? factory.createYieldExpression( 3228 factory.createToken(SyntaxKind.AsteriskToken), 3229 setEmitFlags(call, EmitFlags.Iterator) 3230 ) 3231 : call; 3232 return factory.createExpressionStatement(callResult); 3233 } 3234 3235 function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, state: ConvertedLoopState, outerState: ConvertedLoopState | undefined, containsYield: boolean): Statement[] { 3236 3237 const statements: Statement[] = []; 3238 // loop is considered simple if it does not have any return statements or break\continue that transfer control outside of the loop 3239 // simple loops are emitted as just 'loop()'; 3240 // NOTE: if loop uses only 'continue' it still will be emitted as simple loop 3241 const isSimpleLoop = 3242 !(state.nonLocalJumps! & ~Jump.Continue) && 3243 !state.labeledNonLocalBreaks && 3244 !state.labeledNonLocalContinues; 3245 3246 const call = factory.createCallExpression(loopFunctionExpressionName, /*typeArguments*/ undefined, map(state.loopParameters, p => <Identifier>p.name)); 3247 const callResult = containsYield 3248 ? factory.createYieldExpression( 3249 factory.createToken(SyntaxKind.AsteriskToken), 3250 setEmitFlags(call, EmitFlags.Iterator) 3251 ) 3252 : call; 3253 if (isSimpleLoop) { 3254 statements.push(factory.createExpressionStatement(callResult)); 3255 copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements); 3256 } 3257 else { 3258 const loopResultName = factory.createUniqueName("state"); 3259 const stateVariable = factory.createVariableStatement( 3260 /*modifiers*/ undefined, 3261 factory.createVariableDeclarationList( 3262 [factory.createVariableDeclaration(loopResultName, /*exclamationToken*/ undefined, /*type*/ undefined, callResult)] 3263 ) 3264 ); 3265 statements.push(stateVariable); 3266 copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements); 3267 3268 if (state.nonLocalJumps! & Jump.Return) { 3269 let returnStatement: ReturnStatement; 3270 if (outerState) { 3271 outerState.nonLocalJumps! |= Jump.Return; 3272 returnStatement = factory.createReturnStatement(loopResultName); 3273 } 3274 else { 3275 returnStatement = factory.createReturnStatement(factory.createPropertyAccessExpression(loopResultName, "value")); 3276 } 3277 statements.push( 3278 factory.createIfStatement( 3279 factory.createTypeCheck(loopResultName, "object"), 3280 returnStatement 3281 ) 3282 ); 3283 } 3284 3285 if (state.nonLocalJumps! & Jump.Break) { 3286 statements.push( 3287 factory.createIfStatement( 3288 factory.createStrictEquality( 3289 loopResultName, 3290 factory.createStringLiteral("break") 3291 ), 3292 factory.createBreakStatement() 3293 ) 3294 ); 3295 } 3296 3297 if (state.labeledNonLocalBreaks || state.labeledNonLocalContinues) { 3298 const caseClauses: CaseClause[] = []; 3299 processLabeledJumps(state.labeledNonLocalBreaks!, /*isBreak*/ true, loopResultName, outerState, caseClauses); 3300 processLabeledJumps(state.labeledNonLocalContinues!, /*isBreak*/ false, loopResultName, outerState, caseClauses); 3301 statements.push( 3302 factory.createSwitchStatement( 3303 loopResultName, 3304 factory.createCaseBlock(caseClauses) 3305 ) 3306 ); 3307 } 3308 } 3309 return statements; 3310 } 3311 3312 function setLabeledJump(state: ConvertedLoopState, isBreak: boolean, labelText: string, labelMarker: string): void { 3313 if (isBreak) { 3314 if (!state.labeledNonLocalBreaks) { 3315 state.labeledNonLocalBreaks = new Map<string, string>(); 3316 } 3317 state.labeledNonLocalBreaks.set(labelText, labelMarker); 3318 } 3319 else { 3320 if (!state.labeledNonLocalContinues) { 3321 state.labeledNonLocalContinues = new Map<string, string>(); 3322 } 3323 state.labeledNonLocalContinues.set(labelText, labelMarker); 3324 } 3325 } 3326 3327 function processLabeledJumps(table: ESMap<string, string>, isBreak: boolean, loopResultName: Identifier, outerLoop: ConvertedLoopState | undefined, caseClauses: CaseClause[]): void { 3328 if (!table) { 3329 return; 3330 } 3331 table.forEach((labelMarker, labelText) => { 3332 const statements: Statement[] = []; 3333 // if there are no outer converted loop or outer label in question is located inside outer converted loop 3334 // then emit labeled break\continue 3335 // otherwise propagate pair 'label -> marker' to outer converted loop and emit 'return labelMarker' so outer loop can later decide what to do 3336 if (!outerLoop || (outerLoop.labels && outerLoop.labels.get(labelText))) { 3337 const label = factory.createIdentifier(labelText); 3338 statements.push(isBreak ? factory.createBreakStatement(label) : factory.createContinueStatement(label)); 3339 } 3340 else { 3341 setLabeledJump(outerLoop, isBreak, labelText, labelMarker); 3342 statements.push(factory.createReturnStatement(loopResultName)); 3343 } 3344 caseClauses.push(factory.createCaseClause(factory.createStringLiteral(labelMarker), statements)); 3345 }); 3346 } 3347 3348 function processLoopVariableDeclaration(container: IterationStatement, decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[], hasCapturedBindingsInForInitializer: boolean) { 3349 const name = decl.name; 3350 if (isBindingPattern(name)) { 3351 for (const element of name.elements) { 3352 if (!isOmittedExpression(element)) { 3353 processLoopVariableDeclaration(container, element, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer); 3354 } 3355 } 3356 } 3357 else { 3358 loopParameters.push(factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name)); 3359 const checkFlags = resolver.getNodeCheckFlags(decl); 3360 if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter || hasCapturedBindingsInForInitializer) { 3361 const outParamName = factory.createUniqueName("out_" + idText(name)); 3362 let flags: LoopOutParameterFlags = 0; 3363 if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter) { 3364 flags |= LoopOutParameterFlags.Body; 3365 } 3366 if (isForStatement(container) && container.initializer && resolver.isBindingCapturedByNode(container.initializer, decl)) { 3367 flags |= LoopOutParameterFlags.Initializer; 3368 } 3369 loopOutParameters.push({ flags, originalName: name, outParamName }); 3370 } 3371 } 3372 } 3373 3374 /** 3375 * Adds the members of an object literal to an array of expressions. 3376 * 3377 * @param expressions An array of expressions. 3378 * @param node An ObjectLiteralExpression node. 3379 * @param receiver The receiver for members of the ObjectLiteralExpression. 3380 * @param numInitialNonComputedProperties The number of initial properties without 3381 * computed property names. 3382 */ 3383 function addObjectLiteralMembers(expressions: Expression[], node: ObjectLiteralExpression, receiver: Identifier, start: number) { 3384 const properties = node.properties; 3385 const numProperties = properties.length; 3386 for (let i = start; i < numProperties; i++) { 3387 const property = properties[i]; 3388 switch (property.kind) { 3389 case SyntaxKind.GetAccessor: 3390 case SyntaxKind.SetAccessor: 3391 const accessors = getAllAccessorDeclarations(node.properties, property); 3392 if (property === accessors.firstAccessor) { 3393 expressions.push(transformAccessorsToExpression(receiver, accessors, node, !!node.multiLine)); 3394 } 3395 3396 break; 3397 3398 case SyntaxKind.MethodDeclaration: 3399 expressions.push(transformObjectLiteralMethodDeclarationToExpression(property, receiver, node, node.multiLine!)); 3400 break; 3401 3402 case SyntaxKind.PropertyAssignment: 3403 expressions.push(transformPropertyAssignmentToExpression(property, receiver, node.multiLine!)); 3404 break; 3405 3406 case SyntaxKind.ShorthandPropertyAssignment: 3407 expressions.push(transformShorthandPropertyAssignmentToExpression(property, receiver, node.multiLine!)); 3408 break; 3409 3410 default: 3411 Debug.failBadSyntaxKind(node); 3412 break; 3413 } 3414 } 3415 } 3416 3417 /** 3418 * Transforms a PropertyAssignment node into an expression. 3419 * 3420 * @param node The ObjectLiteralExpression that contains the PropertyAssignment. 3421 * @param property The PropertyAssignment node. 3422 * @param receiver The receiver for the assignment. 3423 */ 3424 function transformPropertyAssignmentToExpression(property: PropertyAssignment, receiver: Expression, startsOnNewLine: boolean) { 3425 const expression = factory.createAssignment( 3426 createMemberAccessForPropertyName( 3427 factory, 3428 receiver, 3429 visitNode(property.name, visitor, isPropertyName) 3430 ), 3431 visitNode(property.initializer, visitor, isExpression) 3432 ); 3433 setTextRange(expression, property); 3434 if (startsOnNewLine) { 3435 startOnNewLine(expression); 3436 } 3437 return expression; 3438 } 3439 3440 /** 3441 * Transforms a ShorthandPropertyAssignment node into an expression. 3442 * 3443 * @param node The ObjectLiteralExpression that contains the ShorthandPropertyAssignment. 3444 * @param property The ShorthandPropertyAssignment node. 3445 * @param receiver The receiver for the assignment. 3446 */ 3447 function transformShorthandPropertyAssignmentToExpression(property: ShorthandPropertyAssignment, receiver: Expression, startsOnNewLine: boolean) { 3448 const expression = factory.createAssignment( 3449 createMemberAccessForPropertyName( 3450 factory, 3451 receiver, 3452 visitNode(property.name, visitor, isPropertyName) 3453 ), 3454 factory.cloneNode(property.name) 3455 ); 3456 setTextRange(expression, property); 3457 if (startsOnNewLine) { 3458 startOnNewLine(expression); 3459 } 3460 return expression; 3461 } 3462 3463 /** 3464 * Transforms a MethodDeclaration of an ObjectLiteralExpression into an expression. 3465 * 3466 * @param node The ObjectLiteralExpression that contains the MethodDeclaration. 3467 * @param method The MethodDeclaration node. 3468 * @param receiver The receiver for the assignment. 3469 */ 3470 function transformObjectLiteralMethodDeclarationToExpression(method: MethodDeclaration, receiver: Expression, container: Node, startsOnNewLine: boolean) { 3471 const expression = factory.createAssignment( 3472 createMemberAccessForPropertyName( 3473 factory, 3474 receiver, 3475 visitNode(method.name, visitor, isPropertyName) 3476 ), 3477 transformFunctionLikeToExpression(method, /*location*/ method, /*name*/ undefined, container) 3478 ); 3479 setTextRange(expression, method); 3480 if (startsOnNewLine) { 3481 startOnNewLine(expression); 3482 } 3483 return expression; 3484 } 3485 3486 function visitCatchClause(node: CatchClause): CatchClause { 3487 const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes); 3488 let updated: CatchClause; 3489 Debug.assert(!!node.variableDeclaration, "Catch clause variable should always be present when downleveling ES2015."); 3490 if (isBindingPattern(node.variableDeclaration.name)) { 3491 const temp = factory.createTempVariable(/*recordTempVariable*/ undefined); 3492 const newVariableDeclaration = factory.createVariableDeclaration(temp); 3493 setTextRange(newVariableDeclaration, node.variableDeclaration); 3494 const vars = flattenDestructuringBinding( 3495 node.variableDeclaration, 3496 visitor, 3497 context, 3498 FlattenLevel.All, 3499 temp 3500 ); 3501 const list = factory.createVariableDeclarationList(vars); 3502 setTextRange(list, node.variableDeclaration); 3503 const destructure = factory.createVariableStatement(/*modifiers*/ undefined, list); 3504 updated = factory.updateCatchClause(node, newVariableDeclaration, addStatementToStartOfBlock(node.block, destructure)); 3505 } 3506 else { 3507 updated = visitEachChild(node, visitor, context); 3508 } 3509 3510 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 3511 return updated; 3512 } 3513 3514 function addStatementToStartOfBlock(block: Block, statement: Statement): Block { 3515 const transformedStatements = visitNodes(block.statements, visitor, isStatement); 3516 return factory.updateBlock(block, [statement, ...transformedStatements]); 3517 } 3518 3519 /** 3520 * Visits a MethodDeclaration of an ObjectLiteralExpression and transforms it into a 3521 * PropertyAssignment. 3522 * 3523 * @param node A MethodDeclaration node. 3524 */ 3525 function visitMethodDeclaration(node: MethodDeclaration): ObjectLiteralElementLike { 3526 // We should only get here for methods on an object literal with regular identifier names. 3527 // Methods on classes are handled in visitClassDeclaration/visitClassExpression. 3528 // Methods with computed property names are handled in visitObjectLiteralExpression. 3529 Debug.assert(!isComputedPropertyName(node.name)); 3530 const functionExpression = transformFunctionLikeToExpression(node, /*location*/ moveRangePos(node, -1), /*name*/ undefined, /*container*/ undefined); 3531 setEmitFlags(functionExpression, EmitFlags.NoLeadingComments | getEmitFlags(functionExpression)); 3532 return setTextRange( 3533 factory.createPropertyAssignment( 3534 node.name, 3535 functionExpression 3536 ), 3537 /*location*/ node 3538 ); 3539 } 3540 3541 /** 3542 * Visits an AccessorDeclaration of an ObjectLiteralExpression. 3543 * 3544 * @param node An AccessorDeclaration node. 3545 */ 3546 function visitAccessorDeclaration(node: AccessorDeclaration): AccessorDeclaration { 3547 Debug.assert(!isComputedPropertyName(node.name)); 3548 const savedConvertedLoopState = convertedLoopState; 3549 convertedLoopState = undefined; 3550 const ancestorFacts = enterSubtree(HierarchyFacts.FunctionExcludes, HierarchyFacts.FunctionIncludes); 3551 let updated: AccessorDeclaration; 3552 const parameters = visitParameterList(node.parameters, visitor, context); 3553 const body = transformFunctionBody(node); 3554 if (node.kind === SyntaxKind.GetAccessor) { 3555 updated = factory.updateGetAccessorDeclaration(node, node.decorators, node.modifiers, node.name, parameters, node.type, body); 3556 } 3557 else { 3558 updated = factory.updateSetAccessorDeclaration(node, node.decorators, node.modifiers, node.name, parameters, body); 3559 } 3560 exitSubtree(ancestorFacts, HierarchyFacts.FunctionSubtreeExcludes, HierarchyFacts.None); 3561 convertedLoopState = savedConvertedLoopState; 3562 return updated; 3563 } 3564 3565 /** 3566 * Visits a ShorthandPropertyAssignment and transforms it into a PropertyAssignment. 3567 * 3568 * @param node A ShorthandPropertyAssignment node. 3569 */ 3570 function visitShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElementLike { 3571 return setTextRange( 3572 factory.createPropertyAssignment( 3573 node.name, 3574 visitIdentifier(factory.cloneNode(node.name)) 3575 ), 3576 /*location*/ node 3577 ); 3578 } 3579 3580 function visitComputedPropertyName(node: ComputedPropertyName) { 3581 return visitEachChild(node, visitor, context); 3582 } 3583 3584 /** 3585 * Visits a YieldExpression node. 3586 * 3587 * @param node A YieldExpression node. 3588 */ 3589 function visitYieldExpression(node: YieldExpression): Expression { 3590 // `yield` expressions are transformed using the generators transformer. 3591 return visitEachChild(node, visitor, context); 3592 } 3593 3594 /** 3595 * Visits an ArrayLiteralExpression that contains a spread element. 3596 * 3597 * @param node An ArrayLiteralExpression node. 3598 */ 3599 function visitArrayLiteralExpression(node: ArrayLiteralExpression): Expression { 3600 if (some(node.elements, isSpreadElement)) { 3601 // We are here because we contain a SpreadElementExpression. 3602 return transformAndSpreadElements(node.elements, /*needsUniqueCopy*/ true, !!node.multiLine, /*hasTrailingComma*/ !!node.elements.hasTrailingComma); 3603 } 3604 return visitEachChild(node, visitor, context); 3605 } 3606 3607 /** 3608 * Visits a CallExpression that contains either a spread element or `super`. 3609 * 3610 * @param node a CallExpression. 3611 */ 3612 function visitCallExpression(node: CallExpression) { 3613 if (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) { 3614 return visitTypeScriptClassWrapper(node); 3615 } 3616 3617 const expression = skipOuterExpressions(node.expression); 3618 if (expression.kind === SyntaxKind.SuperKeyword || 3619 isSuperProperty(expression) || 3620 some(node.arguments, isSpreadElement)) { 3621 return visitCallExpressionWithPotentialCapturedThisAssignment(node, /*assignToCapturedThis*/ true); 3622 } 3623 3624 return factory.updateCallExpression( 3625 node, 3626 visitNode(node.expression, callExpressionVisitor, isExpression), 3627 /*typeArguments*/ undefined, 3628 visitNodes(node.arguments, visitor, isExpression) 3629 ); 3630 } 3631 3632 function visitTypeScriptClassWrapper(node: CallExpression) { 3633 // This is a call to a class wrapper function (an IIFE) created by the 'ts' transformer. 3634 // The wrapper has a form similar to: 3635 // 3636 // (function() { 3637 // class C { // 1 3638 // } 3639 // C.x = 1; // 2 3640 // return C; 3641 // }()) 3642 // 3643 // When we transform the class, we end up with something like this: 3644 // 3645 // (function () { 3646 // var C = (function () { // 3 3647 // function C() { 3648 // } 3649 // return C; // 4 3650 // }()); 3651 // C.x = 1; 3652 // return C; 3653 // }()) 3654 // 3655 // We want to simplify the two nested IIFEs to end up with something like this: 3656 // 3657 // (function () { 3658 // function C() { 3659 // } 3660 // C.x = 1; 3661 // return C; 3662 // }()) 3663 3664 // We skip any outer expressions in a number of places to get to the innermost 3665 // expression, but we will restore them later to preserve comments and source maps. 3666 const body = cast(cast(skipOuterExpressions(node.expression), isArrowFunction).body, isBlock); 3667 3668 // The class statements are the statements generated by visiting the first statement with initializer of the 3669 // body (1), while all other statements are added to remainingStatements (2) 3670 const isVariableStatementWithInitializer = (stmt: Statement) => isVariableStatement(stmt) && !!first(stmt.declarationList.declarations).initializer; 3671 3672 // visit the class body statements outside of any converted loop body. 3673 const savedConvertedLoopState = convertedLoopState; 3674 convertedLoopState = undefined; 3675 const bodyStatements = visitNodes(body.statements, visitor, isStatement); 3676 convertedLoopState = savedConvertedLoopState; 3677 3678 const classStatements = filter(bodyStatements, isVariableStatementWithInitializer); 3679 const remainingStatements = filter(bodyStatements, stmt => !isVariableStatementWithInitializer(stmt)); 3680 const varStatement = cast(first(classStatements), isVariableStatement); 3681 3682 // We know there is only one variable declaration here as we verified this in an 3683 // earlier call to isTypeScriptClassWrapper 3684 const variable = varStatement.declarationList.declarations[0]; 3685 const initializer = skipOuterExpressions(variable.initializer!); 3686 3687 // Under certain conditions, the 'ts' transformer may introduce a class alias, which 3688 // we see as an assignment, for example: 3689 // 3690 // (function () { 3691 // var C_1; 3692 // var C = C_1 = (function () { 3693 // function C() { 3694 // } 3695 // C.x = function () { return C_1; } 3696 // return C; 3697 // }()); 3698 // C = C_1 = __decorate([dec], C); 3699 // return C; 3700 // }()) 3701 // 3702 const aliasAssignment = tryCast(initializer, isAssignmentExpression); 3703 3704 // The underlying call (3) is another IIFE that may contain a '_super' argument. 3705 const call = cast(aliasAssignment ? skipOuterExpressions(aliasAssignment.right) : initializer, isCallExpression); 3706 const func = cast(skipOuterExpressions(call.expression), isFunctionExpression); 3707 3708 const funcStatements = func.body.statements; 3709 let classBodyStart = 0; 3710 let classBodyEnd = -1; 3711 3712 const statements: Statement[] = []; 3713 if (aliasAssignment) { 3714 // If we have a class alias assignment, we need to move it to the down-level constructor 3715 // function we generated for the class. 3716 const extendsCall = tryCast(funcStatements[classBodyStart], isExpressionStatement); 3717 if (extendsCall) { 3718 statements.push(extendsCall); 3719 classBodyStart++; 3720 } 3721 3722 // The next statement is the function declaration. 3723 statements.push(funcStatements[classBodyStart]); 3724 classBodyStart++; 3725 3726 // Add the class alias following the declaration. 3727 statements.push( 3728 factory.createExpressionStatement( 3729 factory.createAssignment( 3730 aliasAssignment.left, 3731 cast(variable.name, isIdentifier) 3732 ) 3733 ) 3734 ); 3735 } 3736 3737 // Find the trailing 'return' statement (4) 3738 while (!isReturnStatement(elementAt(funcStatements, classBodyEnd)!)) { 3739 classBodyEnd--; 3740 } 3741 3742 // When we extract the statements of the inner IIFE, we exclude the 'return' statement (4) 3743 // as we already have one that has been introduced by the 'ts' transformer. 3744 addRange(statements, funcStatements, classBodyStart, classBodyEnd); 3745 3746 if (classBodyEnd < -1) { 3747 // If there were any hoisted declarations following the return statement, we should 3748 // append them. 3749 addRange(statements, funcStatements, classBodyEnd + 1); 3750 } 3751 3752 // Add the remaining statements of the outer wrapper. 3753 addRange(statements, remainingStatements); 3754 3755 // The 'es2015' class transform may add an end-of-declaration marker. If so we will add it 3756 // after the remaining statements from the 'ts' transformer. 3757 addRange(statements, classStatements, /*start*/ 1); 3758 3759 // Recreate any outer parentheses or partially-emitted expressions to preserve source map 3760 // and comment locations. 3761 return factory.restoreOuterExpressions(node.expression, 3762 factory.restoreOuterExpressions(variable.initializer, 3763 factory.restoreOuterExpressions(aliasAssignment && aliasAssignment.right, 3764 factory.updateCallExpression(call, 3765 factory.restoreOuterExpressions(call.expression, 3766 factory.updateFunctionExpression( 3767 func, 3768 /*modifiers*/ undefined, 3769 /*asteriskToken*/ undefined, 3770 /*name*/ undefined, 3771 /*typeParameters*/ undefined, 3772 func.parameters, 3773 /*type*/ undefined, 3774 factory.updateBlock( 3775 func.body, 3776 statements 3777 ) 3778 ) 3779 ), 3780 /*typeArguments*/ undefined, 3781 call.arguments 3782 ) 3783 ) 3784 ) 3785 ); 3786 } 3787 3788 function visitImmediateSuperCallInBody(node: CallExpression) { 3789 return visitCallExpressionWithPotentialCapturedThisAssignment(node, /*assignToCapturedThis*/ false); 3790 } 3791 3792 function visitCallExpressionWithPotentialCapturedThisAssignment(node: CallExpression, assignToCapturedThis: boolean): CallExpression | BinaryExpression { 3793 // We are here either because SuperKeyword was used somewhere in the expression, or 3794 // because we contain a SpreadElementExpression. 3795 if (node.transformFlags & TransformFlags.ContainsRestOrSpread || 3796 node.expression.kind === SyntaxKind.SuperKeyword || 3797 isSuperProperty(skipOuterExpressions(node.expression))) { 3798 3799 const { target, thisArg } = factory.createCallBinding(node.expression, hoistVariableDeclaration); 3800 if (node.expression.kind === SyntaxKind.SuperKeyword) { 3801 setEmitFlags(thisArg, EmitFlags.NoSubstitution); 3802 } 3803 3804 let resultingCall: CallExpression | BinaryExpression; 3805 if (node.transformFlags & TransformFlags.ContainsRestOrSpread) { 3806 // [source] 3807 // f(...a, b) 3808 // x.m(...a, b) 3809 // super(...a, b) 3810 // super.m(...a, b) // in static 3811 // super.m(...a, b) // in instance 3812 // 3813 // [output] 3814 // f.apply(void 0, a.concat([b])) 3815 // (_a = x).m.apply(_a, a.concat([b])) 3816 // _super.apply(this, a.concat([b])) 3817 // _super.m.apply(this, a.concat([b])) 3818 // _super.prototype.m.apply(this, a.concat([b])) 3819 3820 resultingCall = factory.createFunctionApplyCall( 3821 visitNode(target, callExpressionVisitor, isExpression), 3822 node.expression.kind === SyntaxKind.SuperKeyword ? thisArg : visitNode(thisArg, visitor, isExpression), 3823 transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*hasTrailingComma*/ false) 3824 ); 3825 } 3826 else { 3827 // [source] 3828 // super(a) 3829 // super.m(a) // in static 3830 // super.m(a) // in instance 3831 // 3832 // [output] 3833 // _super.call(this, a) 3834 // _super.m.call(this, a) 3835 // _super.prototype.m.call(this, a) 3836 resultingCall = setTextRange( 3837 factory.createFunctionCallCall( 3838 visitNode(target, callExpressionVisitor, isExpression), 3839 node.expression.kind === SyntaxKind.SuperKeyword ? thisArg : visitNode(thisArg, visitor, isExpression), 3840 visitNodes(node.arguments, visitor, isExpression) 3841 ), 3842 node 3843 ); 3844 } 3845 3846 if (node.expression.kind === SyntaxKind.SuperKeyword) { 3847 const initializer = 3848 factory.createLogicalOr( 3849 resultingCall, 3850 createActualThis() 3851 ); 3852 resultingCall = assignToCapturedThis 3853 ? factory.createAssignment(factory.createUniqueName("_this", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), initializer) 3854 : initializer; 3855 } 3856 return setOriginalNode(resultingCall, node); 3857 } 3858 3859 return visitEachChild(node, visitor, context); 3860 } 3861 3862 /** 3863 * Visits a NewExpression that contains a spread element. 3864 * 3865 * @param node A NewExpression node. 3866 */ 3867 function visitNewExpression(node: NewExpression): LeftHandSideExpression { 3868 if (some(node.arguments, isSpreadElement)) { 3869 // We are here because we contain a SpreadElementExpression. 3870 // [source] 3871 // new C(...a) 3872 // 3873 // [output] 3874 // new ((_a = C).bind.apply(_a, [void 0].concat(a)))() 3875 3876 const { target, thisArg } = factory.createCallBinding(factory.createPropertyAccessExpression(node.expression, "bind"), hoistVariableDeclaration); 3877 return factory.createNewExpression( 3878 factory.createFunctionApplyCall( 3879 visitNode(target, visitor, isExpression), 3880 thisArg, 3881 transformAndSpreadElements(factory.createNodeArray([factory.createVoidZero(), ...node.arguments!]), /*needsUniqueCopy*/ false, /*multiLine*/ false, /*hasTrailingComma*/ false) 3882 ), 3883 /*typeArguments*/ undefined, 3884 [] 3885 ); 3886 } 3887 return visitEachChild(node, visitor, context); 3888 } 3889 3890 /** 3891 * Transforms an array of Expression nodes that contains a SpreadExpression. 3892 * 3893 * @param elements The array of Expression nodes. 3894 * @param needsUniqueCopy A value indicating whether to ensure that the result is a fresh array. 3895 * This should be `true` when spreading into an `ArrayLiteral`, and `false` when spreading into an 3896 * argument list. 3897 * @param multiLine A value indicating whether the result should be emitted on multiple lines. 3898 */ 3899 function transformAndSpreadElements(elements: NodeArray<Expression>, needsUniqueCopy: boolean, multiLine: boolean, hasTrailingComma: boolean): Expression { 3900 // When there is no leading SpreadElement: 3901 // 3902 // [source] 3903 // [a, ...b, c] 3904 // 3905 // [output (downlevelIteration)] 3906 // __spreadArray(__spreadArray([a], __read(b)), [c]) 3907 // 3908 // [output] 3909 // __spreadArray(__spreadArray([a], b), [c]) 3910 // 3911 // When there *is* a leading SpreadElement: 3912 // 3913 // [source] 3914 // [...a, b] 3915 // 3916 // [output (downlevelIteration)] 3917 // __spreadArray(__spreadArray([], __read(a)), [b]) 3918 // 3919 // [output] 3920 // __spreadArray(__spreadArray([], a), [b]) 3921 // 3922 // NOTE: We use `isPackedArrayLiteral` below rather than just `isArrayLiteral` 3923 // because ES2015 spread will replace _missing_ array elements with `undefined`, 3924 // so we cannot just use an array as is. For example: 3925 // 3926 // `[1, ...[2, , 3]]` becomes `[1, 2, undefined, 3]` 3927 // 3928 // However, for packed array literals (i.e., an array literal with no OmittedExpression 3929 // elements), we can use the array as-is. 3930 3931 // Map spans of spread expressions into their expressions and spans of other 3932 // expressions into an array literal. 3933 const numElements = elements.length; 3934 const segments = flatten<Expression>( 3935 spanMap(elements, partitionSpread, (partition, visitPartition, _start, end) => 3936 visitPartition(partition, multiLine, hasTrailingComma && end === numElements) 3937 ) 3938 ); 3939 3940 if (segments.length === 1) { 3941 const firstSegment = segments[0]; 3942 // If we don't need a unique copy, then we are spreading into an argument list for 3943 // a CallExpression or NewExpression. When using `--downlevelIteration`, we need 3944 // to coerce this into an array for use with `apply`, so we will use the code path 3945 // that follows instead. 3946 if (!needsUniqueCopy && !compilerOptions.downlevelIteration 3947 || isPackedArrayLiteral(firstSegment) // see NOTE (above) 3948 || isCallToHelper(firstSegment, "___spreadArray" as __String)) { 3949 return segments[0]; 3950 } 3951 } 3952 3953 const helpers = emitHelpers(); 3954 const startsWithSpread = isSpreadElement(elements[0]); 3955 let expression: Expression = 3956 startsWithSpread ? factory.createArrayLiteralExpression() : 3957 segments[0]; 3958 for (let i = startsWithSpread ? 0 : 1; i < segments.length; i++) { 3959 expression = helpers.createSpreadArrayHelper( 3960 expression, 3961 compilerOptions.downlevelIteration && !isPackedArrayLiteral(segments[i]) ? // see NOTE (above) 3962 helpers.createReadHelper(segments[i], /*count*/ undefined) : 3963 segments[i]); 3964 } 3965 3966 return expression; 3967 } 3968 3969 function partitionSpread(node: Expression) { 3970 return isSpreadElement(node) 3971 ? visitSpanOfSpreads 3972 : visitSpanOfNonSpreads; 3973 } 3974 3975 function visitSpanOfSpreads(chunk: Expression[]): VisitResult<Expression> { 3976 return map(chunk, visitExpressionOfSpread); 3977 } 3978 3979 function visitSpanOfNonSpreads(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult<Expression> { 3980 return factory.createArrayLiteralExpression( 3981 visitNodes(factory.createNodeArray(chunk, hasTrailingComma), visitor, isExpression), 3982 multiLine 3983 ); 3984 } 3985 3986 function visitSpreadElement(node: SpreadElement) { 3987 return visitNode(node.expression, visitor, isExpression); 3988 } 3989 3990 /** 3991 * Transforms the expression of a SpreadExpression node. 3992 * 3993 * @param node A SpreadExpression node. 3994 */ 3995 function visitExpressionOfSpread(node: SpreadElement) { 3996 return visitNode(node.expression, visitor, isExpression); 3997 } 3998 3999 /** 4000 * Visits a template literal. 4001 * 4002 * @param node A template literal. 4003 */ 4004 function visitTemplateLiteral(node: LiteralExpression): LeftHandSideExpression { 4005 return setTextRange(factory.createStringLiteral(node.text), node); 4006 } 4007 4008 /** 4009 * Visits a string literal with an extended unicode escape. 4010 * 4011 * @param node A string literal. 4012 */ 4013 function visitStringLiteral(node: StringLiteral) { 4014 if (node.hasExtendedUnicodeEscape) { 4015 return setTextRange(factory.createStringLiteral(node.text), node); 4016 } 4017 return node; 4018 } 4019 4020 /** 4021 * Visits a binary or octal (ES6) numeric literal. 4022 * 4023 * @param node A string literal. 4024 */ 4025 function visitNumericLiteral(node: NumericLiteral) { 4026 if (node.numericLiteralFlags & TokenFlags.BinaryOrOctalSpecifier) { 4027 return setTextRange(factory.createNumericLiteral(node.text), node); 4028 } 4029 return node; 4030 } 4031 4032 /** 4033 * Visits a TaggedTemplateExpression node. 4034 * 4035 * @param node A TaggedTemplateExpression node. 4036 */ 4037 function visitTaggedTemplateExpression(node: TaggedTemplateExpression) { 4038 return processTaggedTemplateExpression( 4039 context, 4040 node, 4041 visitor, 4042 currentSourceFile, 4043 recordTaggedTemplateString, 4044 ProcessLevel.All 4045 ); 4046 } 4047 4048 /** 4049 * Visits a TemplateExpression node. 4050 * 4051 * @param node A TemplateExpression node. 4052 */ 4053 function visitTemplateExpression(node: TemplateExpression): Expression { 4054 const expressions: Expression[] = []; 4055 addTemplateHead(expressions, node); 4056 addTemplateSpans(expressions, node); 4057 4058 // createAdd will check if each expression binds less closely than binary '+'. 4059 // If it does, it wraps the expression in parentheses. Otherwise, something like 4060 // `abc${ 1 << 2 }` 4061 // becomes 4062 // "abc" + 1 << 2 + "" 4063 // which is really 4064 // ("abc" + 1) << (2 + "") 4065 // rather than 4066 // "abc" + (1 << 2) + "" 4067 const expression = reduceLeft(expressions, factory.createAdd)!; 4068 if (nodeIsSynthesized(expression)) { 4069 setTextRange(expression, node); 4070 } 4071 4072 return expression; 4073 } 4074 4075 /** 4076 * Gets a value indicating whether we need to include the head of a TemplateExpression. 4077 * 4078 * @param node A TemplateExpression node. 4079 */ 4080 function shouldAddTemplateHead(node: TemplateExpression) { 4081 // If this expression has an empty head literal and the first template span has a non-empty 4082 // literal, then emitting the empty head literal is not necessary. 4083 // `${ foo } and ${ bar }` 4084 // can be emitted as 4085 // foo + " and " + bar 4086 // This is because it is only required that one of the first two operands in the emit 4087 // output must be a string literal, so that the other operand and all following operands 4088 // are forced into strings. 4089 // 4090 // If the first template span has an empty literal, then the head must still be emitted. 4091 // `${ foo }${ bar }` 4092 // must still be emitted as 4093 // "" + foo + bar 4094 4095 // There is always atleast one templateSpan in this code path, since 4096 // NoSubstitutionTemplateLiterals are directly emitted via emitLiteral() 4097 Debug.assert(node.templateSpans.length !== 0); 4098 4099 return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0; 4100 } 4101 4102 /** 4103 * Adds the head of a TemplateExpression to an array of expressions. 4104 * 4105 * @param expressions An array of expressions. 4106 * @param node A TemplateExpression node. 4107 */ 4108 function addTemplateHead(expressions: Expression[], node: TemplateExpression): void { 4109 if (!shouldAddTemplateHead(node)) { 4110 return; 4111 } 4112 4113 expressions.push(factory.createStringLiteral(node.head.text)); 4114 } 4115 4116 /** 4117 * Visits and adds the template spans of a TemplateExpression to an array of expressions. 4118 * 4119 * @param expressions An array of expressions. 4120 * @param node A TemplateExpression node. 4121 */ 4122 function addTemplateSpans(expressions: Expression[], node: TemplateExpression): void { 4123 for (const span of node.templateSpans) { 4124 expressions.push(visitNode(span.expression, visitor, isExpression)); 4125 4126 // Only emit if the literal is non-empty. 4127 // The binary '+' operator is left-associative, so the first string concatenation 4128 // with the head will force the result up to this point to be a string. 4129 // Emitting a '+ ""' has no semantic effect for middles and tails. 4130 if (span.literal.text.length !== 0) { 4131 expressions.push(factory.createStringLiteral(span.literal.text)); 4132 } 4133 } 4134 } 4135 4136 /** 4137 * Visits the `super` keyword 4138 */ 4139 function visitSuperKeyword(isExpressionOfCall: boolean): LeftHandSideExpression { 4140 return hierarchyFacts & HierarchyFacts.NonStaticClassElement 4141 && !isExpressionOfCall 4142 ? factory.createPropertyAccessExpression(factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), "prototype") 4143 : factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel); 4144 } 4145 4146 function visitMetaProperty(node: MetaProperty) { 4147 if (node.keywordToken === SyntaxKind.NewKeyword && node.name.escapedText === "target") { 4148 hierarchyFacts |= HierarchyFacts.NewTarget; 4149 return factory.createUniqueName("_newTarget", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel); 4150 } 4151 return node; 4152 } 4153 4154 /** 4155 * Called by the printer just before a node is printed. 4156 * 4157 * @param hint A hint as to the intended usage of the node. 4158 * @param node The node to be printed. 4159 * @param emitCallback The callback used to emit the node. 4160 */ 4161 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { 4162 if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) { 4163 // If we are tracking a captured `this`, keep track of the enclosing function. 4164 const ancestorFacts = enterSubtree( 4165 HierarchyFacts.FunctionExcludes, 4166 getEmitFlags(node) & EmitFlags.CapturesThis 4167 ? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis 4168 : HierarchyFacts.FunctionIncludes); 4169 previousOnEmitNode(hint, node, emitCallback); 4170 exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); 4171 return; 4172 } 4173 previousOnEmitNode(hint, node, emitCallback); 4174 } 4175 4176 /** 4177 * Enables a more costly code path for substitutions when we determine a source file 4178 * contains block-scoped bindings (e.g. `let` or `const`). 4179 */ 4180 function enableSubstitutionsForBlockScopedBindings() { 4181 if ((enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) === 0) { 4182 enabledSubstitutions |= ES2015SubstitutionFlags.BlockScopedBindings; 4183 context.enableSubstitution(SyntaxKind.Identifier); 4184 } 4185 } 4186 4187 /** 4188 * Enables a more costly code path for substitutions when we determine a source file 4189 * contains a captured `this`. 4190 */ 4191 function enableSubstitutionsForCapturedThis() { 4192 if ((enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis) === 0) { 4193 enabledSubstitutions |= ES2015SubstitutionFlags.CapturedThis; 4194 context.enableSubstitution(SyntaxKind.ThisKeyword); 4195 context.enableEmitNotification(SyntaxKind.Constructor); 4196 context.enableEmitNotification(SyntaxKind.MethodDeclaration); 4197 context.enableEmitNotification(SyntaxKind.GetAccessor); 4198 context.enableEmitNotification(SyntaxKind.SetAccessor); 4199 context.enableEmitNotification(SyntaxKind.ArrowFunction); 4200 context.enableEmitNotification(SyntaxKind.FunctionExpression); 4201 context.enableEmitNotification(SyntaxKind.FunctionDeclaration); 4202 } 4203 } 4204 4205 /** 4206 * Hooks node substitutions. 4207 * 4208 * @param hint The context for the emitter. 4209 * @param node The node to substitute. 4210 */ 4211 function onSubstituteNode(hint: EmitHint, node: Node) { 4212 node = previousOnSubstituteNode(hint, node); 4213 4214 if (hint === EmitHint.Expression) { 4215 return substituteExpression(node); 4216 } 4217 4218 if (isIdentifier(node)) { 4219 return substituteIdentifier(node); 4220 } 4221 4222 return node; 4223 } 4224 4225 /** 4226 * Hooks substitutions for non-expression identifiers. 4227 */ 4228 function substituteIdentifier(node: Identifier) { 4229 // Only substitute the identifier if we have enabled substitutions for block-scoped 4230 // bindings. 4231 if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) { 4232 const original = getParseTreeNode(node, isIdentifier); 4233 if (original && isNameOfDeclarationWithCollidingName(original)) { 4234 return setTextRange(factory.getGeneratedNameForNode(original), node); 4235 } 4236 } 4237 4238 return node; 4239 } 4240 4241 /** 4242 * Determines whether a name is the name of a declaration with a colliding name. 4243 * NOTE: This function expects to be called with an original source tree node. 4244 * 4245 * @param node An original source tree node. 4246 */ 4247 function isNameOfDeclarationWithCollidingName(node: Identifier) { 4248 switch (node.parent.kind) { 4249 case SyntaxKind.BindingElement: 4250 case SyntaxKind.ClassDeclaration: 4251 case SyntaxKind.EnumDeclaration: 4252 case SyntaxKind.VariableDeclaration: 4253 return (<NamedDeclaration>node.parent).name === node 4254 && resolver.isDeclarationWithCollidingName(<Declaration>node.parent); 4255 } 4256 4257 return false; 4258 } 4259 4260 /** 4261 * Substitutes an expression. 4262 * 4263 * @param node An Expression node. 4264 */ 4265 function substituteExpression(node: Node) { 4266 switch (node.kind) { 4267 case SyntaxKind.Identifier: 4268 return substituteExpressionIdentifier(<Identifier>node); 4269 4270 case SyntaxKind.ThisKeyword: 4271 return substituteThisKeyword(<PrimaryExpression>node); 4272 } 4273 4274 return node; 4275 } 4276 4277 /** 4278 * Substitutes an expression identifier. 4279 * 4280 * @param node An Identifier node. 4281 */ 4282 function substituteExpressionIdentifier(node: Identifier): Identifier { 4283 if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) { 4284 const declaration = resolver.getReferencedDeclarationWithCollidingName(node); 4285 if (declaration && !(isClassLike(declaration) && isPartOfClassBody(declaration, node))) { 4286 return setTextRange(factory.getGeneratedNameForNode(getNameOfDeclaration(declaration)), node); 4287 } 4288 } 4289 4290 return node; 4291 } 4292 4293 function isPartOfClassBody(declaration: ClassLikeDeclaration, node: Identifier) { 4294 let currentNode: Node | undefined = getParseTreeNode(node); 4295 if (!currentNode || currentNode === declaration || currentNode.end <= declaration.pos || currentNode.pos >= declaration.end) { 4296 // if the node has no correlation to a parse tree node, its definitely not 4297 // part of the body. 4298 // if the node is outside of the document range of the declaration, its 4299 // definitely not part of the body. 4300 return false; 4301 } 4302 const blockScope = getEnclosingBlockScopeContainer(declaration); 4303 while (currentNode) { 4304 if (currentNode === blockScope || currentNode === declaration) { 4305 // if we are in the enclosing block scope of the declaration, we are definitely 4306 // not inside the class body. 4307 return false; 4308 } 4309 if (isClassElement(currentNode) && currentNode.parent === declaration) { 4310 return true; 4311 } 4312 currentNode = currentNode.parent; 4313 } 4314 return false; 4315 } 4316 4317 /** 4318 * Substitutes `this` when contained within an arrow function. 4319 * 4320 * @param node The ThisKeyword node. 4321 */ 4322 function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression { 4323 if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis 4324 && hierarchyFacts & HierarchyFacts.CapturesThis) { 4325 return setTextRange(factory.createUniqueName("_this", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel), node); 4326 } 4327 return node; 4328 } 4329 4330 function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) { 4331 return hasSyntacticModifier(member, ModifierFlags.Static) 4332 ? factory.getInternalName(node) 4333 : factory.createPropertyAccessExpression(factory.getInternalName(node), "prototype"); 4334 } 4335 4336 function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration | undefined, hasExtendsClause: boolean) { 4337 if (!constructor || !hasExtendsClause) { 4338 return false; 4339 } 4340 4341 if (some(constructor.parameters)) { 4342 return false; 4343 } 4344 4345 const statement = firstOrUndefined(constructor.body!.statements); 4346 if (!statement || !nodeIsSynthesized(statement) || statement.kind !== SyntaxKind.ExpressionStatement) { 4347 return false; 4348 } 4349 4350 const statementExpression = (<ExpressionStatement>statement).expression; 4351 if (!nodeIsSynthesized(statementExpression) || statementExpression.kind !== SyntaxKind.CallExpression) { 4352 return false; 4353 } 4354 4355 const callTarget = (<CallExpression>statementExpression).expression; 4356 if (!nodeIsSynthesized(callTarget) || callTarget.kind !== SyntaxKind.SuperKeyword) { 4357 return false; 4358 } 4359 4360 const callArgument = singleOrUndefined((<CallExpression>statementExpression).arguments); 4361 if (!callArgument || !nodeIsSynthesized(callArgument) || callArgument.kind !== SyntaxKind.SpreadElement) { 4362 return false; 4363 } 4364 4365 const expression = (<SpreadElement>callArgument).expression; 4366 return isIdentifier(expression) && expression.escapedText === "arguments"; 4367 } 4368 } 4369} 4370