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