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