1// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 2// See LICENSE.txt in the project root for complete license information. 3 4///<reference path='typescript.ts' /> 5 6module TypeScript { 7 export class ASTSpan { 8 public minChar: number = -1; // -1 = "undefined" or "compiler generated" 9 public limChar: number = -1; // -1 = "undefined" or "compiler generated" 10 } 11 12 export class AST extends ASTSpan { 13 public type: Type = null; 14 public flags = ASTFlags.Writeable; 15 16 // REVIEW: for diagnostic purposes 17 public passCreated: number = CompilerDiagnostics.analysisPass; 18 19 public preComments: Comment[] = null; 20 public postComments: Comment[] = null; 21 22 public isParenthesized = false; 23 24 constructor (public nodeType: NodeType) { 25 super(); 26 } 27 28 public isExpression() { return false; } 29 30 public isStatementOrExpression() { return false; } 31 32 public isCompoundStatement() { return false; } 33 34 public isLeaf() { return this.isStatementOrExpression() && (!this.isCompoundStatement()); } 35 36 public typeCheck(typeFlow: TypeFlow) { 37 switch (this.nodeType) { 38 case NodeType.Error: 39 case NodeType.EmptyExpr: 40 this.type = typeFlow.anyType; 41 break; 42 case NodeType.This: 43 return typeFlow.typeCheckThis(this); 44 case NodeType.Null: 45 this.type = typeFlow.nullType; 46 break; 47 case NodeType.False: 48 case NodeType.True: 49 this.type = typeFlow.booleanType; 50 break; 51 case NodeType.Super: 52 return typeFlow.typeCheckSuper(this); 53 case NodeType.EndCode: 54 case NodeType.Empty: 55 case NodeType.Void: 56 this.type = typeFlow.voidType; 57 break; 58 default: 59 throw new Error("please implement in derived class"); 60 } 61 return this; 62 } 63 64 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 65 emitter.emitParensAndCommentsInPlace(this, true); 66 switch (this.nodeType) { 67 case NodeType.This: 68 emitter.recordSourceMappingStart(this); 69 if (emitter.thisFnc && (hasFlag(emitter.thisFnc.fncFlags, FncFlags.IsFatArrowFunction))) { 70 emitter.writeToOutput("_this"); 71 } 72 else { 73 emitter.writeToOutput("this"); 74 } 75 emitter.recordSourceMappingEnd(this); 76 break; 77 case NodeType.Null: 78 emitter.recordSourceMappingStart(this); 79 emitter.writeToOutput("null"); 80 emitter.recordSourceMappingEnd(this); 81 break; 82 case NodeType.False: 83 emitter.recordSourceMappingStart(this); 84 emitter.writeToOutput("false"); 85 emitter.recordSourceMappingEnd(this); 86 break; 87 case NodeType.True: 88 emitter.recordSourceMappingStart(this); 89 emitter.writeToOutput("true"); 90 emitter.recordSourceMappingEnd(this); 91 break; 92 case NodeType.Super: 93 emitter.recordSourceMappingStart(this); 94 emitter.emitSuperReference(); 95 emitter.recordSourceMappingEnd(this); 96 break; 97 case NodeType.EndCode: 98 break; 99 case NodeType.Error: 100 case NodeType.EmptyExpr: 101 break; 102 103 case NodeType.Empty: 104 emitter.recordSourceMappingStart(this); 105 emitter.writeToOutput("; "); 106 emitter.recordSourceMappingEnd(this); 107 break; 108 case NodeType.Void: 109 emitter.recordSourceMappingStart(this); 110 emitter.writeToOutput("void "); 111 emitter.recordSourceMappingEnd(this); 112 break; 113 default: 114 throw new Error("please implement in derived class"); 115 } 116 emitter.emitParensAndCommentsInPlace(this, false); 117 } 118 119 public print(context: PrintContext) { 120 context.startLine(); 121 var lineCol = { line: -1, col: -1 }; 122 var limLineCol = { line: -1, col: -1 }; 123 if (context.parser !== null) { 124 context.parser.getSourceLineCol(lineCol, this.minChar); 125 context.parser.getSourceLineCol(limLineCol, this.limChar); 126 context.write("(" + lineCol.line + "," + lineCol.col + ")--" + 127 "(" + limLineCol.line + "," + limLineCol.col + "): "); 128 } 129 var lab = this.printLabel(); 130 if (hasFlag(this.flags, ASTFlags.Error)) { 131 lab += " (Error)"; 132 } 133 context.writeLine(lab); 134 } 135 136 public printLabel() { 137 if (nodeTypeTable[this.nodeType] !== undefined) { 138 return nodeTypeTable[this.nodeType]; 139 } 140 else { 141 return (<any>NodeType)._map[this.nodeType]; 142 } 143 } 144 145 public addToControlFlow(context: ControlFlowContext): void { 146 // by default, AST adds itself to current basic block and does not check its children 147 context.walker.options.goChildren = false; 148 context.addContent(this); 149 } 150 151 public netFreeUses(container: Symbol, freeUses: StringHashTable) { 152 } 153 154 public treeViewLabel() { 155 return (<any>NodeType)._map[this.nodeType]; 156 } 157 158 public static getResolvedIdentifierName(name: string): string { 159 if (!name) return ""; 160 161 var resolved = ""; 162 var start = 0; 163 var i = 0; 164 while(i <= name.length - 6) { 165 // Look for escape sequence \uxxxx 166 if (name.charAt(i) == '\\' && name.charAt(i+1) == 'u') { 167 var charCode = parseInt(name.substr(i + 2, 4), 16); 168 resolved += name.substr(start, i - start); 169 resolved += String.fromCharCode(charCode); 170 i += 6; 171 start = i; 172 continue; 173 } 174 i++; 175 } 176 // Append remaining string 177 resolved += name.substring(start); 178 return resolved; 179 } 180 } 181 182 export class IncompleteAST extends AST { 183 constructor (min: number, lim: number) { 184 super(NodeType.Error); 185 186 this.minChar = min; 187 this.limChar = lim; 188 } 189 } 190 191 export class ASTList extends AST { 192 public enclosingScope: SymbolScope = null; 193 public members: AST[] = new AST[]; 194 195 constructor () { 196 super(NodeType.List); 197 } 198 199 public addToControlFlow(context: ControlFlowContext) { 200 var len = this.members.length; 201 for (var i = 0; i < len; i++) { 202 if (context.noContinuation) { 203 context.addUnreachable(this.members[i]); 204 break; 205 } 206 else { 207 this.members[i] = context.walk(this.members[i], this); 208 } 209 } 210 context.walker.options.goChildren = false; 211 } 212 213 public append(ast: AST) { 214 this.members[this.members.length] = ast; 215 return this; 216 } 217 218 public appendAll(ast: AST) { 219 if (ast.nodeType == NodeType.List) { 220 var list = <ASTList>ast; 221 for (var i = 0, len = list.members.length; i < len; i++) { 222 this.append(list.members[i]); 223 } 224 } 225 else { 226 this.append(ast); 227 } 228 return this; 229 } 230 231 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 232 emitter.recordSourceMappingStart(this); 233 emitter.emitJavascriptList(this, null, TokenID.Semicolon, startLine, false, false); 234 emitter.recordSourceMappingEnd(this); 235 } 236 237 public typeCheck(typeFlow: TypeFlow) { 238 var len = this.members.length; 239 typeFlow.nestingLevel++; 240 for (var i = 0; i < len; i++) { 241 if (this.members[i]) { 242 this.members[i] = this.members[i].typeCheck(typeFlow); 243 } 244 } 245 typeFlow.nestingLevel--; 246 return this; 247 } 248 } 249 250 export class Identifier extends AST { 251 public sym: Symbol = null; 252 public cloId = -1; 253 public text: string; 254 255 // 'actualText' is the text that the user has entered for the identifier. the text might 256 // include any Unicode escape sequences (e.g.: \u0041 for 'A'). 'text', however, contains 257 // the resolved value of any escape sequences in the actual text; so in the previous 258 // example, actualText = '\u0041', text = 'A'. 259 // 260 // For purposes of finding a symbol, use text, as this will allow you to match all 261 // variations of the variable text. For full-fidelity translation of the user input, such 262 // as emitting, use the actualText field. 263 // 264 // Note: 265 // To change text, and to avoid running into a situation where 'actualText' does not 266 // match 'text', always use setText. 267 constructor (public actualText: string, public hasEscapeSequence?: boolean) { 268 super(NodeType.Name); 269 this.setText(actualText, hasEscapeSequence); 270 } 271 272 public setText(actualText: string, hasEscapeSequence?: boolean) { 273 this.actualText = actualText; 274 if (hasEscapeSequence) { 275 this.text = AST.getResolvedIdentifierName(actualText); 276 } 277 else { 278 this.text = actualText; 279 } 280 } 281 282 public isMissing() { return false; } 283 public isLeaf() { return true; } 284 285 public treeViewLabel() { 286 return "id: " + this.actualText; 287 } 288 289 public printLabel() { 290 if (this.actualText) { 291 return "id: " + this.actualText; 292 } 293 else { 294 return "name node"; 295 } 296 } 297 298 public typeCheck(typeFlow: TypeFlow) { 299 return typeFlow.typeCheckName(this); 300 } 301 302 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 303 emitter.emitJavascriptName(this, true); 304 } 305 306 public static fromToken(token: Token): Identifier { 307 return new Identifier(token.getText(), (<IdentifierToken>token).hasEscapeSequence); 308 } 309 } 310 311 export class MissingIdentifier extends Identifier { 312 constructor () { 313 super("__missing"); 314 } 315 316 public isMissing() { 317 return true; 318 } 319 320 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 321 // Emit nothing for a missing ID 322 } 323 } 324 325 export class Label extends AST { 326 constructor (public id: Identifier) { 327 super(NodeType.Label); 328 } 329 330 public printLabel() { return this.id.actualText + ":"; } 331 332 public typeCheck(typeFlow: TypeFlow) { 333 this.type = typeFlow.voidType; 334 return this; 335 } 336 337 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 338 emitter.emitParensAndCommentsInPlace(this, true); 339 emitter.recordSourceMappingStart(this); 340 emitter.recordSourceMappingStart(this.id); 341 emitter.writeToOutput(this.id.actualText); 342 emitter.recordSourceMappingEnd(this.id); 343 emitter.writeLineToOutput(":"); 344 emitter.recordSourceMappingEnd(this); 345 emitter.emitParensAndCommentsInPlace(this, false); 346 } 347 } 348 349 export class Expression extends AST { 350 constructor (nodeType: NodeType) { 351 super(nodeType); 352 } 353 354 public isExpression() { return true; } 355 356 public isStatementOrExpression() { return true; } 357 } 358 359 export class UnaryExpression extends Expression { 360 public targetType: Type = null; // Target type for an object literal (null if no target type) 361 public castTerm: AST = null; 362 363 constructor (nodeType: NodeType, public operand: AST) { 364 super(nodeType); 365 } 366 367 public addToControlFlow(context: ControlFlowContext): void { 368 super.addToControlFlow(context); 369 // TODO: add successor as catch block/finally block if present 370 if (this.nodeType == NodeType.Throw) { 371 context.returnStmt(); 372 } 373 } 374 375 public typeCheck(typeFlow: TypeFlow) { 376 switch (this.nodeType) { 377 case NodeType.Not: 378 return typeFlow.typeCheckBitNot(this); 379 380 case NodeType.LogNot: 381 return typeFlow.typeCheckLogNot(this); 382 383 case NodeType.Pos: 384 case NodeType.Neg: 385 return typeFlow.typeCheckUnaryNumberOperator(this); 386 387 case NodeType.IncPost: 388 case NodeType.IncPre: 389 case NodeType.DecPost: 390 case NodeType.DecPre: 391 return typeFlow.typeCheckIncOrDec(this); 392 393 case NodeType.ArrayLit: 394 typeFlow.typeCheckArrayLit(this); 395 return this; 396 397 case NodeType.ObjectLit: 398 typeFlow.typeCheckObjectLit(this); 399 return this; 400 401 case NodeType.Throw: 402 this.operand = typeFlow.typeCheck(this.operand); 403 this.type = typeFlow.voidType; 404 return this; 405 406 case NodeType.Typeof: 407 this.operand = typeFlow.typeCheck(this.operand); 408 this.type = typeFlow.stringType; 409 return this; 410 411 case NodeType.Delete: 412 this.operand = typeFlow.typeCheck(this.operand); 413 this.type = typeFlow.booleanType; 414 break; 415 416 case NodeType.TypeAssertion: 417 this.castTerm = typeFlow.typeCheck(this.castTerm); 418 var applyTargetType = !this.operand.isParenthesized; 419 420 var targetType = applyTargetType ? this.castTerm.type : null; 421 422 typeFlow.checker.typeCheckWithContextualType(targetType, typeFlow.checker.inProvisionalTypecheckMode(), true, this.operand); 423 typeFlow.castWithCoercion(this.operand, this.castTerm.type, false, true); 424 this.type = this.castTerm.type; 425 return this; 426 427 case NodeType.Void: 428 // REVIEW - Although this is good to do for completeness's sake, 429 // this shouldn't be strictly necessary from the void operator's 430 // point of view 431 this.operand = typeFlow.typeCheck(this.operand); 432 this.type = typeFlow.checker.undefinedType; 433 break; 434 435 default: 436 throw new Error("please implement in derived class"); 437 } 438 return this; 439 } 440 441 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 442 emitter.emitParensAndCommentsInPlace(this, true); 443 emitter.recordSourceMappingStart(this); 444 switch (this.nodeType) { 445 case NodeType.IncPost: 446 emitter.emitJavascript(this.operand, TokenID.PlusPlus, false); 447 emitter.writeToOutput("++"); 448 break; 449 case NodeType.LogNot: 450 emitter.writeToOutput("!"); 451 emitter.emitJavascript(this.operand, TokenID.Exclamation, false); 452 break; 453 case NodeType.DecPost: 454 emitter.emitJavascript(this.operand, TokenID.MinusMinus, false); 455 emitter.writeToOutput("--"); 456 break; 457 case NodeType.ObjectLit: 458 emitter.emitObjectLiteral(<ASTList>this.operand); 459 break; 460 case NodeType.ArrayLit: 461 emitter.emitArrayLiteral(<ASTList>this.operand); 462 break; 463 case NodeType.Not: 464 emitter.writeToOutput("~"); 465 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 466 break; 467 case NodeType.Neg: 468 emitter.writeToOutput("-"); 469 if (this.operand.nodeType == NodeType.Neg) { 470 this.operand.isParenthesized = true; 471 } 472 emitter.emitJavascript(this.operand, TokenID.Minus, false); 473 break; 474 case NodeType.Pos: 475 emitter.writeToOutput("+"); 476 if (this.operand.nodeType == NodeType.Pos) { 477 this.operand.isParenthesized = true; 478 } 479 emitter.emitJavascript(this.operand, TokenID.Plus, false); 480 break; 481 case NodeType.IncPre: 482 emitter.writeToOutput("++"); 483 emitter.emitJavascript(this.operand, TokenID.PlusPlus, false); 484 break; 485 case NodeType.DecPre: 486 emitter.writeToOutput("--"); 487 emitter.emitJavascript(this.operand, TokenID.MinusMinus, false); 488 break; 489 case NodeType.Throw: 490 emitter.writeToOutput("throw "); 491 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 492 emitter.writeToOutput(";"); 493 break; 494 case NodeType.Typeof: 495 emitter.writeToOutput("typeof "); 496 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 497 break; 498 case NodeType.Delete: 499 emitter.writeToOutput("delete "); 500 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 501 break; 502 case NodeType.Void: 503 emitter.writeToOutput("void "); 504 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 505 break; 506 case NodeType.TypeAssertion: 507 emitter.emitJavascript(this.operand, TokenID.Tilde, false); 508 break; 509 default: 510 throw new Error("please implement in derived class"); 511 } 512 emitter.recordSourceMappingEnd(this); 513 emitter.emitParensAndCommentsInPlace(this, false); 514 } 515 } 516 517 export class CallExpression extends Expression { 518 constructor (nodeType: NodeType, 519 public target: AST, 520 public arguments: ASTList) { 521 super(nodeType); 522 this.minChar = this.target.minChar; 523 } 524 525 public signature: Signature = null; 526 527 public typeCheck(typeFlow: TypeFlow) { 528 if (this.nodeType == NodeType.New) { 529 return typeFlow.typeCheckNew(this); 530 } 531 else { 532 return typeFlow.typeCheckCall(this); 533 } 534 } 535 536 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 537 emitter.emitParensAndCommentsInPlace(this, true); 538 emitter.recordSourceMappingStart(this); 539 540 if (this.nodeType == NodeType.New) { 541 emitter.emitNew(this.target, this.arguments); 542 } 543 else { 544 emitter.emitCall(this, this.target, this.arguments); 545 } 546 547 emitter.recordSourceMappingEnd(this); 548 emitter.emitParensAndCommentsInPlace(this, false); 549 } 550 } 551 552 export class BinaryExpression extends Expression { 553 constructor (nodeType: NodeType, public operand1: AST, public operand2: AST) { 554 super(nodeType); 555 } 556 557 public typeCheck(typeFlow: TypeFlow) { 558 switch (this.nodeType) { 559 case NodeType.Dot: 560 return typeFlow.typeCheckDotOperator(this); 561 case NodeType.Asg: 562 return typeFlow.typeCheckAsgOperator(this); 563 case NodeType.Add: 564 case NodeType.Sub: 565 case NodeType.Mul: 566 case NodeType.Div: 567 case NodeType.Mod: 568 case NodeType.Or: 569 case NodeType.And: 570 return typeFlow.typeCheckArithmeticOperator(this, false); 571 case NodeType.Xor: 572 return typeFlow.typeCheckBitwiseOperator(this, false); 573 case NodeType.Ne: 574 case NodeType.Eq: 575 var text: string; 576 if (typeFlow.checker.styleSettings.eqeqeq) { 577 text = nodeTypeTable[this.nodeType]; 578 typeFlow.checker.errorReporter.styleError(this, "use of " + text); 579 } 580 else if (typeFlow.checker.styleSettings.eqnull) { 581 text = nodeTypeTable[this.nodeType]; 582 if ((this.operand2 !== null) && (this.operand2.nodeType == NodeType.Null)) { 583 typeFlow.checker.errorReporter.styleError(this, "use of " + text + " to compare with null"); 584 } 585 } 586 case NodeType.Eqv: 587 case NodeType.NEqv: 588 case NodeType.Lt: 589 case NodeType.Le: 590 case NodeType.Ge: 591 case NodeType.Gt: 592 return typeFlow.typeCheckBooleanOperator(this); 593 case NodeType.Index: 594 return typeFlow.typeCheckIndex(this); 595 case NodeType.Member: 596 this.type = typeFlow.voidType; 597 return this; 598 case NodeType.LogOr: 599 return typeFlow.typeCheckLogOr(this); 600 case NodeType.LogAnd: 601 return typeFlow.typeCheckLogAnd(this); 602 case NodeType.AsgAdd: 603 case NodeType.AsgSub: 604 case NodeType.AsgMul: 605 case NodeType.AsgDiv: 606 case NodeType.AsgMod: 607 case NodeType.AsgOr: 608 case NodeType.AsgAnd: 609 return typeFlow.typeCheckArithmeticOperator(this, true); 610 case NodeType.AsgXor: 611 return typeFlow.typeCheckBitwiseOperator(this, true); 612 case NodeType.Lsh: 613 case NodeType.Rsh: 614 case NodeType.Rs2: 615 return typeFlow.typeCheckShift(this, false); 616 case NodeType.AsgLsh: 617 case NodeType.AsgRsh: 618 case NodeType.AsgRs2: 619 return typeFlow.typeCheckShift(this, true); 620 case NodeType.Comma: 621 return typeFlow.typeCheckCommaOperator(this); 622 case NodeType.InstOf: 623 return typeFlow.typeCheckInstOf(this); 624 case NodeType.In: 625 return typeFlow.typeCheckInOperator(this); 626 case NodeType.From: 627 typeFlow.checker.errorReporter.simpleError(this, "Illegal use of 'from' keyword in binary expression"); 628 break; 629 default: 630 throw new Error("please implement in derived class"); 631 } 632 return this; 633 } 634 635 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 636 var binTokenId = nodeTypeToTokTable[this.nodeType]; 637 638 emitter.emitParensAndCommentsInPlace(this, true); 639 emitter.recordSourceMappingStart(this); 640 if (binTokenId != undefined) { 641 642 emitter.emitJavascript(this.operand1, binTokenId, false); 643 644 if (tokenTable[binTokenId].text == "instanceof") { 645 emitter.writeToOutput(" instanceof "); 646 } 647 else if (tokenTable[binTokenId].text == "in") { 648 emitter.writeToOutput(" in "); 649 } 650 else { 651 emitter.writeToOutputTrimmable(" " + tokenTable[binTokenId].text + " "); 652 } 653 654 emitter.emitJavascript(this.operand2, binTokenId, false); 655 } 656 else { 657 switch (this.nodeType) { 658 case NodeType.Dot: 659 if (!emitter.tryEmitConstant(this)) { 660 emitter.emitJavascript(this.operand1, TokenID.Dot, false); 661 emitter.writeToOutput("."); 662 emitter.emitJavascriptName(<Identifier>this.operand2, false); 663 } 664 break; 665 case NodeType.Index: 666 emitter.emitIndex(this.operand1, this.operand2); 667 break; 668 669 case NodeType.Member: 670 if (this.operand2.nodeType == NodeType.FuncDecl && (<FuncDecl>this.operand2).isAccessor()) { 671 var funcDecl = <FuncDecl>this.operand2; 672 if (hasFlag(funcDecl.fncFlags, FncFlags.GetAccessor)) { 673 emitter.writeToOutput("get "); 674 } 675 else { 676 emitter.writeToOutput("set "); 677 } 678 emitter.emitJavascript(this.operand1, TokenID.Colon, false); 679 } 680 else { 681 emitter.emitJavascript(this.operand1, TokenID.Colon, false); 682 emitter.writeToOutputTrimmable(": "); 683 } 684 emitter.emitJavascript(this.operand2, TokenID.Comma, false); 685 break; 686 case NodeType.Comma: 687 emitter.emitJavascript(this.operand1, TokenID.Comma, false); 688 if (emitter.emitState.inObjectLiteral) { 689 emitter.writeLineToOutput(", "); 690 } 691 else { 692 emitter.writeToOutput(","); 693 } 694 emitter.emitJavascript(this.operand2, TokenID.Comma, false); 695 break; 696 case NodeType.Is: 697 throw new Error("should be de-sugared during type check"); 698 default: 699 throw new Error("please implement in derived class"); 700 } 701 } 702 emitter.recordSourceMappingEnd(this); 703 emitter.emitParensAndCommentsInPlace(this, false); 704 } 705 } 706 707 export class ConditionalExpression extends Expression { 708 constructor (public operand1: AST, 709 public operand2: AST, 710 public operand3: AST) { 711 super(NodeType.ConditionalExpression); 712 } 713 714 public typeCheck(typeFlow: TypeFlow) { 715 return typeFlow.typeCheckQMark(this); 716 } 717 718 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 719 emitter.emitParensAndCommentsInPlace(this, true); 720 emitter.recordSourceMappingStart(this); 721 emitter.emitJavascript(this.operand1, TokenID.Question, false); 722 emitter.writeToOutput(" ? "); 723 emitter.emitJavascript(this.operand2, TokenID.Question, false); 724 emitter.writeToOutput(" : "); 725 emitter.emitJavascript(this.operand3, TokenID.Question, false); 726 emitter.recordSourceMappingEnd(this); 727 emitter.emitParensAndCommentsInPlace(this, false); 728 } 729 } 730 731 export class NumberLiteral extends Expression { 732 constructor (public value: number, public hasEmptyFraction?: boolean) { 733 super(NodeType.NumberLit); 734 } 735 736 public isNegativeZero = false; 737 738 public typeCheck(typeFlow: TypeFlow) { 739 this.type = typeFlow.doubleType; 740 return this; 741 } 742 743 public treeViewLabel() { 744 return "num: " + this.printLabel(); 745 } 746 747 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 748 emitter.emitParensAndCommentsInPlace(this, true); 749 emitter.recordSourceMappingStart(this); 750 if (this.isNegativeZero) { 751 emitter.writeToOutput("-"); 752 } 753 754 emitter.writeToOutput(this.value.toString()); 755 756 if (this.hasEmptyFraction) 757 emitter.writeToOutput(".0"); 758 759 emitter.recordSourceMappingEnd(this); 760 emitter.emitParensAndCommentsInPlace(this, false); 761 } 762 763 public printLabel() { 764 if (Math.floor(this.value) != this.value) { 765 return this.value.toFixed(2).toString(); 766 } 767 else if (this.hasEmptyFraction) { 768 return this.value.toString() + ".0"; 769 } 770 else { 771 return this.value.toString(); 772 } 773 } 774 } 775 776 export class RegexLiteral extends Expression { 777 constructor (public regex) { 778 super(NodeType.Regex); 779 } 780 781 public typeCheck(typeFlow: TypeFlow) { 782 this.type = typeFlow.regexType; 783 return this; 784 } 785 786 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 787 emitter.emitParensAndCommentsInPlace(this, true); 788 emitter.recordSourceMappingStart(this); 789 emitter.writeToOutput(this.regex.toString()); 790 emitter.recordSourceMappingEnd(this); 791 emitter.emitParensAndCommentsInPlace(this, false); 792 } 793 } 794 795 export class StringLiteral extends Expression { 796 constructor (public text: string) { 797 super(NodeType.QString); 798 } 799 800 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 801 emitter.emitParensAndCommentsInPlace(this, true); 802 emitter.recordSourceMappingStart(this); 803 emitter.emitStringLiteral(this.text); 804 emitter.recordSourceMappingEnd(this); 805 emitter.emitParensAndCommentsInPlace(this, false); 806 } 807 808 public typeCheck(typeFlow: TypeFlow) { 809 this.type = typeFlow.stringType; 810 return this; 811 } 812 813 public treeViewLabel() { 814 return "st: " + this.text; 815 } 816 817 public printLabel() { 818 return this.text; 819 } 820 } 821 822 export class ModuleElement extends AST { 823 constructor (nodeType: NodeType) { 824 super(nodeType); 825 } 826 } 827 828 export class ImportDeclaration extends ModuleElement { 829 public isStatementOrExpression() { return true; } 830 public varFlags = VarFlags.None; 831 public isDynamicImport = false; 832 833 constructor (public id: Identifier, public alias: AST) { 834 super(NodeType.ImportDeclaration); 835 } 836 837 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 838 var mod = <ModuleType>this.alias.type; 839 // REVIEW: Only modules may be aliased for now, though there's no real 840 // restriction on what the type symbol may be 841 if (!this.isDynamicImport || (this.id.sym && !(<TypeSymbol>this.id.sym).onlyReferencedAsTypeRef)) { 842 var prevModAliasId = emitter.modAliasId; 843 var prevFirstModAlias = emitter.firstModAlias; 844 845 emitter.recordSourceMappingStart(this); 846 emitter.emitParensAndCommentsInPlace(this, true); 847 emitter.writeToOutput("var " + this.id.actualText + " = "); 848 emitter.modAliasId = this.id.actualText; 849 emitter.firstModAlias = this.firstAliasedModToString(); 850 emitter.emitJavascript(this.alias, TokenID.Tilde, false); 851 // the dynamic import case will insert the semi-colon automatically 852 if (!this.isDynamicImport) { 853 emitter.writeToOutput(";"); 854 } 855 emitter.emitParensAndCommentsInPlace(this, false); 856 emitter.recordSourceMappingEnd(this); 857 858 emitter.modAliasId = prevModAliasId; 859 emitter.firstModAlias = prevFirstModAlias; 860 } 861 } 862 863 public typeCheck(typeFlow: TypeFlow) { 864 return typeFlow.typeCheckImportDecl(this); 865 } 866 867 public getAliasName(aliasAST?: AST = this.alias) : string { 868 if (aliasAST.nodeType == NodeType.Name) { 869 return (<Identifier>aliasAST).actualText; 870 } else { 871 var dotExpr = <BinaryExpression>aliasAST; 872 return this.getAliasName(dotExpr.operand1) + "." + this.getAliasName(dotExpr.operand2); 873 } 874 } 875 876 public firstAliasedModToString() { 877 if (this.alias.nodeType == NodeType.Name) { 878 return (<Identifier>this.alias).actualText; 879 } 880 else { 881 var dotExpr = <BinaryExpression>this.alias; 882 var firstMod = <Identifier>dotExpr.operand1; 883 return firstMod.actualText; 884 } 885 } 886 } 887 888 export class BoundDecl extends AST { 889 public init: AST = null; 890 public typeExpr: AST = null; 891 public varFlags = VarFlags.None; 892 public sym: Symbol = null; 893 894 constructor (public id: Identifier, nodeType: NodeType, public nestingLevel: number) { 895 super(nodeType); 896 } 897 898 public isStatementOrExpression() { return true; } 899 900 public isPrivate() { return hasFlag(this.varFlags, VarFlags.Private); } 901 public isPublic() { return hasFlag(this.varFlags, VarFlags.Public); } 902 public isProperty() { return hasFlag(this.varFlags, VarFlags.Property); } 903 904 public typeCheck(typeFlow: TypeFlow) { 905 return typeFlow.typeCheckBoundDecl(this); 906 } 907 908 public printLabel() { 909 return this.treeViewLabel(); 910 } 911 } 912 913 export class VarDecl extends BoundDecl { 914 constructor (id: Identifier, nest: number) { 915 super(id, NodeType.VarDecl, nest); 916 } 917 918 public isAmbient() { return hasFlag(this.varFlags, VarFlags.Ambient); } 919 public isExported() { return hasFlag(this.varFlags, VarFlags.Exported); } 920 public isStatic() { return hasFlag(this.varFlags, VarFlags.Static); } 921 922 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 923 emitter.emitJavascriptVarDecl(this, tokenId); 924 } 925 926 public treeViewLabel() { 927 return "var " + this.id.actualText; 928 } 929 } 930 931 export class ArgDecl extends BoundDecl { 932 constructor (id: Identifier) { 933 super(id, NodeType.ArgDecl, 0); 934 } 935 936 public isOptional = false; 937 938 public isOptionalArg() { return this.isOptional || this.init; } 939 940 public treeViewLabel() { 941 return "arg: " + this.id.actualText; 942 } 943 944 public parameterPropertySym: FieldSymbol = null; 945 946 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 947 emitter.emitParensAndCommentsInPlace(this, true); 948 emitter.recordSourceMappingStart(this); 949 emitter.writeToOutput(this.id.actualText); 950 emitter.recordSourceMappingEnd(this); 951 emitter.emitParensAndCommentsInPlace(this, false); 952 } 953 } 954 955 var internalId = 0; 956 957 export class FuncDecl extends AST { 958 public hint: string = null; 959 public fncFlags = FncFlags.None; 960 public returnTypeAnnotation: AST = null; 961 public symbols: IHashTable; 962 public variableArgList = false; 963 public signature: Signature; 964 public envids: Identifier[]; 965 public jumpRefs: Identifier[] = null; 966 public internalNameCache: string = null; 967 public tmp1Declared = false; 968 public enclosingFnc: FuncDecl = null; 969 public freeVariables: Symbol[] = []; 970 public unitIndex = -1; 971 public classDecl: NamedDeclaration = null; 972 public boundToProperty: VarDecl = null; 973 public isOverload = false; 974 public innerStaticFuncs: FuncDecl[] = []; 975 public isTargetTypedAsMethod = false; 976 public isInlineCallLiteral = false; 977 public accessorSymbol: Symbol = null; 978 public leftCurlyCount = 0; 979 public rightCurlyCount = 0; 980 public returnStatementsWithExpressions: ReturnStatement[] = []; 981 public scopeType: Type = null; // Type of the FuncDecl, before target typing 982 public endingToken: ASTSpan = null; 983 984 constructor (public name: Identifier, public bod: ASTList, public isConstructor: boolean, 985 public arguments: ASTList, public vars: ASTList, public scopes: ASTList, public statics: ASTList, 986 nodeType: number) { 987 988 super(nodeType); 989 } 990 991 public internalName(): string { 992 if (this.internalNameCache == null) { 993 var extName = this.getNameText(); 994 if (extName) { 995 this.internalNameCache = "_internal_" + extName; 996 } 997 else { 998 this.internalNameCache = "_internal_" + internalId++; 999 } 1000 } 1001 return this.internalNameCache; 1002 } 1003 1004 public hasSelfReference() { return hasFlag(this.fncFlags, FncFlags.HasSelfReference); } 1005 public setHasSelfReference() { this.fncFlags |= FncFlags.HasSelfReference; } 1006 1007 public addCloRef(id: Identifier, sym: Symbol): number { 1008 if (this.envids == null) { 1009 this.envids = new Identifier[]; 1010 } 1011 this.envids[this.envids.length] = id; 1012 var outerFnc = this.enclosingFnc; 1013 if (sym) { 1014 while (outerFnc && (outerFnc.type.symbol != sym.container)) { 1015 outerFnc.addJumpRef(sym); 1016 outerFnc = outerFnc.enclosingFnc; 1017 } 1018 } 1019 return this.envids.length - 1; 1020 } 1021 1022 public addJumpRef(sym: Symbol): void { 1023 if (this.jumpRefs == null) { 1024 this.jumpRefs = new Identifier[]; 1025 } 1026 var id = new Identifier(sym.name); 1027 this.jumpRefs[this.jumpRefs.length] = id; 1028 id.sym = sym; 1029 id.cloId = this.addCloRef(id, null); 1030 } 1031 1032 public buildControlFlow(): ControlFlowContext { 1033 var entry = new BasicBlock(); 1034 var exit = new BasicBlock(); 1035 1036 var context = new ControlFlowContext(entry, exit); 1037 1038 var controlFlowPrefix = (ast: AST, parent: AST, walker: IAstWalker) => { 1039 ast.addToControlFlow(walker.state); 1040 return ast; 1041 } 1042 1043 var walker = getAstWalkerFactory().getWalker(controlFlowPrefix, null, null, context); 1044 context.walker = walker; 1045 walker.walk(this.bod, this); 1046 1047 return context; 1048 } 1049 1050 public typeCheck(typeFlow: TypeFlow) { 1051 return typeFlow.typeCheckFunction(this); 1052 } 1053 1054 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1055 emitter.emitJavascriptFunction(this); 1056 } 1057 1058 public getNameText() { 1059 if (this.name) { 1060 return this.name.actualText; 1061 } 1062 else { 1063 return this.hint; 1064 } 1065 } 1066 1067 public isMethod() { 1068 return (this.fncFlags & FncFlags.Method) != FncFlags.None; 1069 } 1070 1071 public isCallMember() { return hasFlag(this.fncFlags, FncFlags.CallMember); } 1072 public isConstructMember() { return hasFlag(this.fncFlags, FncFlags.ConstructMember); } 1073 public isIndexerMember() { return hasFlag(this.fncFlags, FncFlags.IndexerMember); } 1074 public isSpecialFn() { return this.isCallMember() || this.isIndexerMember() || this.isConstructMember(); } 1075 public isAnonymousFn() { return this.name === null; } 1076 public isAccessor() { return hasFlag(this.fncFlags, FncFlags.GetAccessor) || hasFlag(this.fncFlags, FncFlags.SetAccessor); } 1077 public isGetAccessor() { return hasFlag(this.fncFlags, FncFlags.GetAccessor); } 1078 public isSetAccessor() { return hasFlag(this.fncFlags, FncFlags.SetAccessor); } 1079 public isAmbient() { return hasFlag(this.fncFlags, FncFlags.Ambient); } 1080 public isExported() { return hasFlag(this.fncFlags, FncFlags.Exported); } 1081 public isPrivate() { return hasFlag(this.fncFlags, FncFlags.Private); } 1082 public isPublic() { return hasFlag(this.fncFlags, FncFlags.Public); } 1083 public isStatic() { return hasFlag(this.fncFlags, FncFlags.Static); } 1084 1085 public treeViewLabel() { 1086 if (this.name == null) { 1087 return "funcExpr"; 1088 } 1089 else { 1090 return "func: " + this.name.actualText 1091 } 1092 } 1093 1094 public ClearFlags(): void { 1095 this.fncFlags = FncFlags.None; 1096 } 1097 1098 public isSignature() { return (this.fncFlags & FncFlags.Signature) != FncFlags.None; } 1099 1100 public hasStaticDeclarations() { return (!this.isConstructor && (this.statics.members.length > 0 || this.innerStaticFuncs.length > 0)); } 1101 } 1102 1103 export class LocationInfo { 1104 constructor (public filename: string, public lineMap: number[], public unitIndex) { } 1105 } 1106 1107 export var unknownLocationInfo = new LocationInfo("unknown", null, -1); 1108 1109 export class Script extends FuncDecl { 1110 public locationInfo: LocationInfo = null; 1111 public referencedFiles: IFileReference[] = []; 1112 public requiresGlobal = false; 1113 public requiresInherits = false; 1114 public isResident = false; 1115 public isDeclareFile = false; 1116 public hasBeenTypeChecked = false; 1117 public topLevelMod: ModuleDeclaration = null; 1118 public leftCurlyCount = 0; 1119 public rightCurlyCount = 0; 1120 public vars: ASTList; 1121 public scopes: ASTList; 1122 // Remember if the script contains Unicode chars, that is needed when generating code for this script object to decide the output file correct encoding. 1123 public containsUnicodeChar = false; 1124 public containsUnicodeCharInComment = false; 1125 1126 constructor (vars: ASTList, scopes: ASTList) { 1127 super(new Identifier("script"), null, false, null, vars, scopes, null, NodeType.Script); 1128 this.vars = vars; 1129 this.scopes = scopes; 1130 } 1131 1132 public typeCheck(typeFlow: TypeFlow) { 1133 return typeFlow.typeCheckScript(this); 1134 } 1135 1136 public treeViewLabel() { 1137 return "Script"; 1138 } 1139 1140 public emitRequired() { 1141 if (!this.isDeclareFile && !this.isResident && this.bod) { 1142 for (var i = 0, len = this.bod.members.length; i < len; i++) { 1143 var stmt = this.bod.members[i]; 1144 if (stmt.nodeType == NodeType.ModuleDeclaration) { 1145 if (!hasFlag((<ModuleDeclaration>stmt).modFlags, ModuleFlags.ShouldEmitModuleDecl | ModuleFlags.Ambient)) { 1146 return true; 1147 } 1148 } 1149 else if (stmt.nodeType == NodeType.ClassDeclaration) { 1150 if (!hasFlag((<InterfaceDeclaration>stmt).varFlags, VarFlags.Ambient)) { 1151 return true; 1152 } 1153 } 1154 else if (stmt.nodeType == NodeType.VarDecl) { 1155 if (!hasFlag((<VarDecl>stmt).varFlags, VarFlags.Ambient)) { 1156 return true; 1157 } 1158 } 1159 else if (stmt.nodeType == NodeType.FuncDecl) { 1160 if (!(<FuncDecl>stmt).isSignature()) { 1161 return true; 1162 } 1163 } 1164 else if (stmt.nodeType != NodeType.InterfaceDeclaration && stmt.nodeType != NodeType.Empty) { 1165 return true; 1166 } 1167 } 1168 } 1169 return false; 1170 } 1171 1172 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1173 if (this.emitRequired()) { 1174 emitter.emitParensAndCommentsInPlace(this, true); 1175 emitter.recordSourceMappingStart(this); 1176 emitter.emitJavascriptList(this.bod, null, TokenID.Semicolon, true, false, false, true, this.requiresInherits); 1177 emitter.recordSourceMappingEnd(this); 1178 emitter.emitParensAndCommentsInPlace(this, false); 1179 } 1180 } 1181 } 1182 1183 export class NamedDeclaration extends ModuleElement { 1184 public leftCurlyCount = 0; 1185 public rightCurlyCount = 0; 1186 1187 constructor (nodeType: NodeType, 1188 public name: Identifier, 1189 public members: ASTList) { 1190 super(nodeType); 1191 } 1192 } 1193 1194 export class ModuleDeclaration extends NamedDeclaration { 1195 public modFlags = ModuleFlags.ShouldEmitModuleDecl; 1196 public mod: ModuleType; 1197 public prettyName: string; 1198 public amdDependencies: string[] = []; 1199 public vars: ASTList; 1200 public scopes: ASTList; 1201 // Remember if the module contains Unicode chars, that is needed for dynamic module as we will generate a file for each. 1202 public containsUnicodeChar = false; 1203 public containsUnicodeCharInComment = false; 1204 1205 constructor (name: Identifier, members: ASTList, vars: ASTList, scopes: ASTList, public endingToken: ASTSpan) { 1206 super(NodeType.ModuleDeclaration, name, members); 1207 1208 this.vars = vars; 1209 this.scopes = scopes; 1210 this.prettyName = this.name.actualText; 1211 } 1212 1213 public isExported() { return hasFlag(this.modFlags, ModuleFlags.Exported); } 1214 public isAmbient() { return hasFlag(this.modFlags, ModuleFlags.Ambient); } 1215 public isEnum() { return hasFlag(this.modFlags, ModuleFlags.IsEnum); } 1216 1217 public recordNonInterface() { 1218 this.modFlags &= ~ModuleFlags.ShouldEmitModuleDecl; 1219 } 1220 1221 public typeCheck(typeFlow: TypeFlow) { 1222 return typeFlow.typeCheckModule(this); 1223 } 1224 1225 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1226 if (!hasFlag(this.modFlags, ModuleFlags.ShouldEmitModuleDecl)) { 1227 emitter.emitParensAndCommentsInPlace(this, true); 1228 emitter.recordSourceMappingStart(this); 1229 emitter.emitJavascriptModule(this); 1230 emitter.recordSourceMappingEnd(this); 1231 emitter.emitParensAndCommentsInPlace(this, false); 1232 } 1233 } 1234 } 1235 1236 export class TypeDeclaration extends NamedDeclaration { 1237 public varFlags = VarFlags.None; 1238 1239 constructor (nodeType: NodeType, 1240 name: Identifier, 1241 public extendsList: ASTList, 1242 public implementsList: ASTList, 1243 members: ASTList) { 1244 super(nodeType, name, members); 1245 } 1246 1247 public isExported() { 1248 return hasFlag(this.varFlags, VarFlags.Exported); 1249 } 1250 1251 public isAmbient() { 1252 return hasFlag(this.varFlags, VarFlags.Ambient); 1253 } 1254 } 1255 1256 export class ClassDeclaration extends TypeDeclaration { 1257 public knownMemberNames: any = {}; 1258 public constructorDecl: FuncDecl = null; 1259 public constructorNestingLevel = 0; 1260 public endingToken: ASTSpan = null; 1261 1262 constructor (name: Identifier, 1263 members: ASTList, 1264 extendsList: ASTList, 1265 implementsList: ASTList) { 1266 super(NodeType.ClassDeclaration, name, extendsList, implementsList, members); 1267 } 1268 1269 public typeCheck(typeFlow: TypeFlow) { 1270 return typeFlow.typeCheckClass(this); 1271 } 1272 1273 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1274 emitter.emitJavascriptClass(this); 1275 } 1276 } 1277 1278 export class InterfaceDeclaration extends TypeDeclaration { 1279 constructor (name: Identifier, 1280 members: ASTList, 1281 extendsList: ASTList, 1282 implementsList: ASTList) { 1283 super(NodeType.InterfaceDeclaration, name, extendsList, implementsList, members); 1284 } 1285 1286 public typeCheck(typeFlow: TypeFlow) { 1287 return typeFlow.typeCheckInterface(this); 1288 } 1289 1290 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1291 } 1292 } 1293 1294 export class Statement extends ModuleElement { 1295 constructor (nodeType: NodeType) { 1296 super(nodeType); 1297 this.flags |= ASTFlags.IsStatement; 1298 } 1299 1300 public isLoop() { return false; } 1301 1302 public isStatementOrExpression() { return true; } 1303 1304 public isCompoundStatement() { return this.isLoop(); } 1305 1306 public typeCheck(typeFlow: TypeFlow) { 1307 this.type = typeFlow.voidType; 1308 return this; 1309 } 1310 } 1311 1312 export class LabeledStatement extends Statement { 1313 constructor (public labels: ASTList, public stmt: AST) { 1314 super(NodeType.LabeledStatement); 1315 } 1316 1317 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1318 emitter.emitParensAndCommentsInPlace(this, true); 1319 emitter.recordSourceMappingStart(this); 1320 if (this.labels) { 1321 var labelsLen = this.labels.members.length; 1322 for (var i = 0; i < labelsLen; i++) { 1323 this.labels.members[i].emit(emitter, tokenId, startLine); 1324 } 1325 } 1326 this.stmt.emit(emitter, tokenId, true); 1327 emitter.recordSourceMappingEnd(this); 1328 emitter.emitParensAndCommentsInPlace(this, false); 1329 } 1330 1331 public typeCheck(typeFlow: TypeFlow) { 1332 typeFlow.typeCheck(this.labels); 1333 this.stmt = this.stmt.typeCheck(typeFlow); 1334 return this; 1335 } 1336 1337 public addToControlFlow(context: ControlFlowContext): void { 1338 var beforeBB = context.current; 1339 var bb = new BasicBlock(); 1340 context.current = bb; 1341 beforeBB.addSuccessor(bb); 1342 } 1343 } 1344 1345 export class Block extends Statement { 1346 constructor (public statements: ASTList, 1347 public isStatementBlock: boolean) { 1348 super(NodeType.Block); 1349 } 1350 1351 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1352 emitter.emitParensAndCommentsInPlace(this, true); 1353 emitter.recordSourceMappingStart(this); 1354 if (this.isStatementBlock) { 1355 emitter.writeLineToOutput(" {"); 1356 emitter.indenter.increaseIndent(); 1357 } else { 1358 emitter.setInVarBlock(this.statements.members.length); 1359 } 1360 var temp = emitter.setInObjectLiteral(false); 1361 if (this.statements) { 1362 emitter.emitJavascriptList(this.statements, null, TokenID.Semicolon, true, false, false); 1363 } 1364 if (this.isStatementBlock) { 1365 emitter.indenter.decreaseIndent(); 1366 emitter.emitIndent(); 1367 emitter.writeToOutput("}"); 1368 } 1369 emitter.setInObjectLiteral(temp); 1370 emitter.recordSourceMappingEnd(this); 1371 emitter.emitParensAndCommentsInPlace(this, false); 1372 } 1373 1374 public addToControlFlow(context: ControlFlowContext) { 1375 var afterIfNeeded = new BasicBlock(); 1376 context.pushStatement(this, context.current, afterIfNeeded); 1377 if (this.statements) { 1378 context.walk(this.statements, this); 1379 } 1380 context.walker.options.goChildren = false; 1381 context.popStatement(); 1382 if (afterIfNeeded.predecessors.length > 0) { 1383 context.current.addSuccessor(afterIfNeeded); 1384 context.current = afterIfNeeded; 1385 } 1386 } 1387 1388 public typeCheck(typeFlow: TypeFlow) { 1389 if (!typeFlow.checker.styleSettings.emptyBlocks) { 1390 if ((this.statements === null) || (this.statements.members.length == 0)) { 1391 typeFlow.checker.errorReporter.styleError(this, "empty block"); 1392 } 1393 } 1394 1395 typeFlow.typeCheck(this.statements); 1396 return this; 1397 } 1398 } 1399 1400 export class Jump extends Statement { 1401 public target: string = null; 1402 public hasExplicitTarget() { return (this.target); } 1403 public resolvedTarget: Statement = null; 1404 1405 constructor (nodeType: NodeType) { 1406 super(nodeType); 1407 } 1408 1409 public setResolvedTarget(parser: Parser, stmt: Statement): boolean { 1410 if (stmt.isLoop()) { 1411 this.resolvedTarget = stmt; 1412 return true; 1413 } 1414 if (this.nodeType === NodeType.Continue) { 1415 parser.reportParseError("continue statement applies only to loops"); 1416 return false; 1417 } 1418 else { 1419 if ((stmt.nodeType == NodeType.Switch) || this.hasExplicitTarget()) { 1420 this.resolvedTarget = stmt; 1421 return true; 1422 } 1423 else { 1424 parser.reportParseError("break statement with no label can apply only to a loop or switch statement"); 1425 return false; 1426 } 1427 } 1428 } 1429 1430 public addToControlFlow(context: ControlFlowContext): void { 1431 super.addToControlFlow(context); 1432 context.unconditionalBranch(this.resolvedTarget, (this.nodeType == NodeType.Continue)); 1433 } 1434 1435 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1436 emitter.emitParensAndCommentsInPlace(this, true); 1437 emitter.recordSourceMappingStart(this); 1438 if (this.nodeType == NodeType.Break) { 1439 emitter.writeToOutput("break"); 1440 } 1441 else { 1442 emitter.writeToOutput("continue"); 1443 } 1444 if (this.hasExplicitTarget()) { 1445 emitter.writeToOutput(" " + this.target); 1446 } 1447 emitter.recordSourceMappingEnd(this); 1448 emitter.writeToOutput(";"); 1449 emitter.emitParensAndCommentsInPlace(this, false); 1450 } 1451 } 1452 1453 export class WhileStatement extends Statement { 1454 public body: AST = null; 1455 1456 constructor (public cond: AST) { 1457 super(NodeType.While); 1458 } 1459 1460 public isLoop() { return true; } 1461 1462 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1463 emitter.emitParensAndCommentsInPlace(this, true); 1464 emitter.recordSourceMappingStart(this); 1465 var temp = emitter.setInObjectLiteral(false); 1466 emitter.writeToOutput("while("); 1467 emitter.emitJavascript(this.cond, TokenID.While, false); 1468 emitter.writeToOutput(")"); 1469 emitter.emitJavascriptStatements(this.body, false, false); 1470 emitter.setInObjectLiteral(temp); 1471 emitter.recordSourceMappingEnd(this); 1472 emitter.emitParensAndCommentsInPlace(this, false); 1473 } 1474 1475 public typeCheck(typeFlow: TypeFlow) { 1476 return typeFlow.typeCheckWhile(this); 1477 } 1478 1479 public addToControlFlow(context: ControlFlowContext): void { 1480 var loopHeader = context.current; 1481 var loopStart = new BasicBlock(); 1482 var afterLoop = new BasicBlock(); 1483 1484 loopHeader.addSuccessor(loopStart); 1485 context.current = loopStart; 1486 context.addContent(this.cond); 1487 var condBlock = context.current; 1488 var targetInfo: ITargetInfo = null; 1489 if (this.body) { 1490 context.current = new BasicBlock(); 1491 condBlock.addSuccessor(context.current); 1492 context.pushStatement(this, loopStart, afterLoop); 1493 context.walk(this.body, this); 1494 targetInfo = context.popStatement(); 1495 } 1496 if (!(context.noContinuation)) { 1497 var loopEnd = context.current; 1498 loopEnd.addSuccessor(loopStart); 1499 } 1500 context.current = afterLoop; 1501 condBlock.addSuccessor(afterLoop); 1502 // TODO: check for while (true) and then only continue if afterLoop has predecessors 1503 context.noContinuation = false; 1504 context.walker.options.goChildren = false; 1505 } 1506 } 1507 1508 export class DoWhileStatement extends Statement { 1509 public body: AST = null; 1510 public whileAST: AST = null; 1511 public cond: AST = null; 1512 public isLoop() { return true; } 1513 1514 constructor () { 1515 super(NodeType.DoWhile); 1516 } 1517 1518 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1519 emitter.emitParensAndCommentsInPlace(this, true); 1520 emitter.recordSourceMappingStart(this); 1521 var temp = emitter.setInObjectLiteral(false); 1522 emitter.writeToOutput("do"); 1523 emitter.emitJavascriptStatements(this.body, true, false); 1524 emitter.recordSourceMappingStart(this.whileAST); 1525 emitter.writeToOutput("while"); 1526 emitter.recordSourceMappingEnd(this.whileAST); 1527 emitter.writeToOutput('('); 1528 emitter.emitJavascript(this.cond, TokenID.CloseParen, false); 1529 emitter.writeToOutput(")"); 1530 emitter.setInObjectLiteral(temp); 1531 emitter.recordSourceMappingEnd(this); 1532 emitter.emitParensAndCommentsInPlace(this, false); 1533 } 1534 1535 public typeCheck(typeFlow: TypeFlow) { 1536 return typeFlow.typeCheckDoWhile(this); 1537 } 1538 1539 public addToControlFlow(context: ControlFlowContext): void { 1540 var loopHeader = context.current; 1541 var loopStart = new BasicBlock(); 1542 var afterLoop = new BasicBlock(); 1543 loopHeader.addSuccessor(loopStart); 1544 context.current = loopStart; 1545 var targetInfo: ITargetInfo = null; 1546 if (this.body) { 1547 context.pushStatement(this, loopStart, afterLoop); 1548 context.walk(this.body, this); 1549 targetInfo = context.popStatement(); 1550 } 1551 if (!(context.noContinuation)) { 1552 var loopEnd = context.current; 1553 loopEnd.addSuccessor(loopStart); 1554 context.addContent(this.cond); 1555 // TODO: check for while (true) 1556 context.current = afterLoop; 1557 loopEnd.addSuccessor(afterLoop); 1558 } 1559 else { 1560 context.addUnreachable(this.cond); 1561 } 1562 context.walker.options.goChildren = false; 1563 } 1564 } 1565 1566 export class IfStatement extends Statement { 1567 public thenBod: AST; 1568 public elseBod: AST = null; 1569 public statement: ASTSpan = new ASTSpan(); 1570 1571 constructor (public cond: AST) { 1572 super(NodeType.If); 1573 } 1574 1575 public isCompoundStatement() { return true; } 1576 1577 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1578 emitter.emitParensAndCommentsInPlace(this, true); 1579 emitter.recordSourceMappingStart(this); 1580 var temp = emitter.setInObjectLiteral(false); 1581 emitter.recordSourceMappingStart(this.statement); 1582 emitter.writeToOutput("if("); 1583 emitter.emitJavascript(this.cond, TokenID.If, false); 1584 emitter.writeToOutput(")"); 1585 emitter.recordSourceMappingEnd(this.statement); 1586 emitter.emitJavascriptStatements(this.thenBod, true, false); 1587 if (this.elseBod) { 1588 emitter.writeToOutput(" else"); 1589 emitter.emitJavascriptStatements(this.elseBod, true, true); 1590 } 1591 emitter.setInObjectLiteral(temp); 1592 emitter.recordSourceMappingEnd(this); 1593 emitter.emitParensAndCommentsInPlace(this, false); 1594 } 1595 1596 public typeCheck(typeFlow: TypeFlow) { 1597 return typeFlow.typeCheckIf(this); 1598 } 1599 1600 public addToControlFlow(context: ControlFlowContext): void { 1601 this.cond.addToControlFlow(context); 1602 var afterIf = new BasicBlock(); 1603 var beforeIf = context.current; 1604 context.pushStatement(this, beforeIf, afterIf); 1605 var hasContinuation = false; 1606 context.current = new BasicBlock(); 1607 beforeIf.addSuccessor(context.current); 1608 context.walk(this.thenBod, this); 1609 if (!context.noContinuation) { 1610 hasContinuation = true; 1611 context.current.addSuccessor(afterIf); 1612 } 1613 if (this.elseBod) { 1614 // current block will be thenBod 1615 context.current = new BasicBlock(); 1616 context.noContinuation = false; 1617 beforeIf.addSuccessor(context.current); 1618 context.walk(this.elseBod, this); 1619 if (!context.noContinuation) { 1620 hasContinuation = true; 1621 context.current.addSuccessor(afterIf); 1622 } 1623 else { 1624 // thenBod created continuation for if statement 1625 if (hasContinuation) { 1626 context.noContinuation = false; 1627 } 1628 } 1629 } 1630 else { 1631 beforeIf.addSuccessor(afterIf); 1632 context.noContinuation = false; 1633 hasContinuation = true; 1634 } 1635 var targetInfo = context.popStatement(); 1636 if (afterIf.predecessors.length > 0) { 1637 context.noContinuation = false; 1638 hasContinuation = true; 1639 } 1640 if (hasContinuation) { 1641 context.current = afterIf; 1642 } 1643 context.walker.options.goChildren = false; 1644 } 1645 } 1646 1647 export class ReturnStatement extends Statement { 1648 public returnExpression: AST = null; 1649 1650 constructor () { 1651 super(NodeType.Return); 1652 } 1653 1654 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1655 emitter.emitParensAndCommentsInPlace(this, true); 1656 emitter.recordSourceMappingStart(this); 1657 var temp = emitter.setInObjectLiteral(false); 1658 if (this.returnExpression) { 1659 emitter.writeToOutput("return "); 1660 emitter.emitJavascript(this.returnExpression, TokenID.Semicolon, false); 1661 } 1662 else { 1663 emitter.writeToOutput("return;"); 1664 } 1665 emitter.setInObjectLiteral(temp); 1666 emitter.recordSourceMappingEnd(this); 1667 emitter.emitParensAndCommentsInPlace(this, false); 1668 } 1669 1670 public addToControlFlow(context: ControlFlowContext): void { 1671 super.addToControlFlow(context); 1672 context.returnStmt(); 1673 } 1674 1675 public typeCheck(typeFlow: TypeFlow) { 1676 return typeFlow.typeCheckReturn(this); 1677 } 1678 } 1679 1680 export class EndCode extends AST { 1681 constructor () { 1682 super(NodeType.EndCode); 1683 } 1684 } 1685 1686 export class ForInStatement extends Statement { 1687 constructor (public lval: AST, public obj: AST) { 1688 super(NodeType.ForIn); 1689 if (this.lval && (this.lval.nodeType == NodeType.VarDecl)) { 1690 (<BoundDecl>this.lval).varFlags |= VarFlags.AutoInit; 1691 } 1692 } 1693 public statement: ASTSpan = new ASTSpan(); 1694 public body: AST; 1695 1696 public isLoop() { return true; } 1697 1698 public isFiltered() { 1699 if (this.body) { 1700 var singleItem: AST = null; 1701 if (this.body.nodeType == NodeType.List) { 1702 var stmts = <ASTList>this.body; 1703 if (stmts.members.length == 1) { 1704 singleItem = stmts.members[0]; 1705 } 1706 } 1707 else { 1708 singleItem = this.body; 1709 } 1710 // match template for filtering 'own' properties from obj 1711 if (singleItem !== null) { 1712 if (singleItem.nodeType == NodeType.Block) { 1713 var block = <Block>singleItem; 1714 if ((block.statements !== null) && (block.statements.members.length == 1)) { 1715 singleItem = block.statements.members[0]; 1716 } 1717 } 1718 if (singleItem.nodeType == NodeType.If) { 1719 var cond = (<IfStatement>singleItem).cond; 1720 if (cond.nodeType == NodeType.Call) { 1721 var target = (<CallExpression>cond).target; 1722 if (target.nodeType == NodeType.Dot) { 1723 var binex = <BinaryExpression>target; 1724 if ((binex.operand1.nodeType == NodeType.Name) && 1725 (this.obj.nodeType == NodeType.Name) && 1726 ((<Identifier>binex.operand1).actualText == (<Identifier>this.obj).actualText)) { 1727 var prop = <Identifier>binex.operand2; 1728 if (prop.actualText == "hasOwnProperty") { 1729 var args = (<CallExpression>cond).arguments; 1730 if ((args !== null) && (args.members.length == 1)) { 1731 var arg = args.members[0]; 1732 if ((arg.nodeType == NodeType.Name) && 1733 (this.lval.nodeType == NodeType.Name)) { 1734 if (((<Identifier>this.lval).actualText) == (<Identifier>arg).actualText) { 1735 return true; 1736 } 1737 } 1738 } 1739 } 1740 } 1741 } 1742 } 1743 } 1744 } 1745 } 1746 return false; 1747 } 1748 1749 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1750 emitter.emitParensAndCommentsInPlace(this, true); 1751 emitter.recordSourceMappingStart(this); 1752 var temp = emitter.setInObjectLiteral(false); 1753 emitter.recordSourceMappingStart(this.statement); 1754 emitter.writeToOutput("for("); 1755 emitter.emitJavascript(this.lval, TokenID.For, false); 1756 emitter.writeToOutput(" in "); 1757 emitter.emitJavascript(this.obj, TokenID.For, false); 1758 emitter.writeToOutput(")"); 1759 emitter.recordSourceMappingEnd(this.statement); 1760 emitter.emitJavascriptStatements(this.body, true, false); 1761 emitter.setInObjectLiteral(temp); 1762 emitter.recordSourceMappingEnd(this); 1763 emitter.emitParensAndCommentsInPlace(this, false); 1764 } 1765 1766 public typeCheck(typeFlow: TypeFlow) { 1767 if (typeFlow.checker.styleSettings.forin) { 1768 if (!this.isFiltered()) { 1769 typeFlow.checker.errorReporter.styleError(this, "no hasOwnProperty filter"); 1770 } 1771 } 1772 return typeFlow.typeCheckForIn(this); 1773 } 1774 1775 public addToControlFlow(context: ControlFlowContext): void { 1776 if (this.lval) { 1777 context.addContent(this.lval); 1778 } 1779 if (this.obj) { 1780 context.addContent(this.obj); 1781 } 1782 1783 var loopHeader = context.current; 1784 var loopStart = new BasicBlock(); 1785 var afterLoop = new BasicBlock(); 1786 1787 loopHeader.addSuccessor(loopStart); 1788 context.current = loopStart; 1789 if (this.body) { 1790 context.pushStatement(this, loopStart, afterLoop); 1791 context.walk(this.body, this); 1792 context.popStatement(); 1793 } 1794 if (!(context.noContinuation)) { 1795 var loopEnd = context.current; 1796 loopEnd.addSuccessor(loopStart); 1797 } 1798 context.current = afterLoop; 1799 context.noContinuation = false; 1800 loopHeader.addSuccessor(afterLoop); 1801 context.walker.options.goChildren = false; 1802 } 1803 } 1804 1805 export class ForStatement extends Statement { 1806 public cond: AST; 1807 public body: AST; 1808 public incr: AST; 1809 1810 constructor (public init: AST) { 1811 super(NodeType.For); 1812 } 1813 1814 public isLoop() { return true; } 1815 1816 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1817 emitter.emitParensAndCommentsInPlace(this, true); 1818 emitter.recordSourceMappingStart(this); 1819 var temp = emitter.setInObjectLiteral(false); 1820 emitter.writeToOutput("for("); 1821 if (this.init) { 1822 if (this.init.nodeType != NodeType.List) { 1823 emitter.emitJavascript(this.init, TokenID.For, false); 1824 } 1825 else { 1826 emitter.setInVarBlock((<ASTList>this.init).members.length); 1827 emitter.emitJavascriptList(this.init, null, TokenID.For, false, false, false); 1828 } 1829 } 1830 emitter.writeToOutput("; "); 1831 emitter.emitJavascript(this.cond, TokenID.For, false); 1832 emitter.writeToOutput("; "); 1833 emitter.emitJavascript(this.incr, TokenID.For, false); 1834 emitter.writeToOutput(")"); 1835 emitter.emitJavascriptStatements(this.body, true, false); 1836 emitter.setInObjectLiteral(temp); 1837 emitter.recordSourceMappingEnd(this); 1838 emitter.emitParensAndCommentsInPlace(this, false); 1839 } 1840 1841 public typeCheck(typeFlow: TypeFlow) { 1842 return typeFlow.typeCheckFor(this); 1843 } 1844 1845 public addToControlFlow(context: ControlFlowContext): void { 1846 if (this.init) { 1847 context.addContent(this.init); 1848 } 1849 var loopHeader = context.current; 1850 var loopStart = new BasicBlock(); 1851 var afterLoop = new BasicBlock(); 1852 1853 loopHeader.addSuccessor(loopStart); 1854 context.current = loopStart; 1855 var condBlock: BasicBlock = null; 1856 var continueTarget = loopStart; 1857 var incrBB: BasicBlock = null; 1858 if (this.incr) { 1859 incrBB = new BasicBlock(); 1860 continueTarget = incrBB; 1861 } 1862 if (this.cond) { 1863 condBlock = context.current; 1864 context.addContent(this.cond); 1865 context.current = new BasicBlock(); 1866 condBlock.addSuccessor(context.current); 1867 } 1868 var targetInfo: ITargetInfo = null; 1869 if (this.body) { 1870 context.pushStatement(this, continueTarget, afterLoop); 1871 context.walk(this.body, this); 1872 targetInfo = context.popStatement(); 1873 } 1874 if (this.incr) { 1875 if (context.noContinuation) { 1876 if (incrBB.predecessors.length == 0) { 1877 context.addUnreachable(this.incr); 1878 } 1879 } 1880 else { 1881 context.current.addSuccessor(incrBB); 1882 context.current = incrBB; 1883 context.addContent(this.incr); 1884 } 1885 } 1886 var loopEnd = context.current; 1887 if (!(context.noContinuation)) { 1888 loopEnd.addSuccessor(loopStart); 1889 1890 } 1891 if (condBlock) { 1892 condBlock.addSuccessor(afterLoop); 1893 context.noContinuation = false; 1894 } 1895 if (afterLoop.predecessors.length > 0) { 1896 context.noContinuation = false; 1897 context.current = afterLoop; 1898 } 1899 context.walker.options.goChildren = false; 1900 } 1901 } 1902 1903 export class WithStatement extends Statement { 1904 public body: AST; 1905 1906 public isCompoundStatement() { return true; } 1907 1908 public withSym: WithSymbol = null; 1909 1910 constructor (public expr: AST) { 1911 super(NodeType.With); 1912 } 1913 1914 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1915 emitter.emitParensAndCommentsInPlace(this, true); 1916 emitter.recordSourceMappingStart(this); 1917 emitter.writeToOutput("with ("); 1918 if (this.expr) { 1919 emitter.emitJavascript(this.expr, TokenID.With, false); 1920 } 1921 1922 emitter.writeToOutput(")"); 1923 emitter.emitJavascriptStatements(this.body, true, false); 1924 emitter.recordSourceMappingEnd(this); 1925 emitter.emitParensAndCommentsInPlace(this, false); 1926 } 1927 1928 public typeCheck(typeFlow: TypeFlow) { 1929 return typeFlow.typeCheckWith(this); 1930 } 1931 } 1932 1933 export class SwitchStatement extends Statement { 1934 public caseList: ASTList; 1935 public defaultCase: CaseStatement = null; 1936 public statement: ASTSpan = new ASTSpan(); 1937 1938 constructor (public val: AST) { 1939 super(NodeType.Switch); 1940 } 1941 1942 public isCompoundStatement() { return true; } 1943 1944 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 1945 emitter.emitParensAndCommentsInPlace(this, true); 1946 emitter.recordSourceMappingStart(this); 1947 var temp = emitter.setInObjectLiteral(false); 1948 emitter.recordSourceMappingStart(this.statement); 1949 emitter.writeToOutput("switch("); 1950 emitter.emitJavascript(this.val, TokenID.Identifier, false); 1951 emitter.writeToOutput(")"); 1952 emitter.recordSourceMappingEnd(this.statement); 1953 emitter.writeLineToOutput(" {"); 1954 emitter.indenter.increaseIndent(); 1955 var casesLen = this.caseList.members.length; 1956 for (var i = 0; i < casesLen; i++) { 1957 var caseExpr = this.caseList.members[i]; 1958 emitter.emitJavascript(caseExpr, TokenID.Case, true); 1959 emitter.writeLineToOutput(""); 1960 } 1961 emitter.indenter.decreaseIndent(); 1962 emitter.emitIndent(); 1963 emitter.writeToOutput("}"); 1964 emitter.setInObjectLiteral(temp); 1965 emitter.recordSourceMappingEnd(this); 1966 emitter.emitParensAndCommentsInPlace(this, false); 1967 } 1968 1969 public typeCheck(typeFlow: TypeFlow) { 1970 var len = this.caseList.members.length; 1971 this.val = typeFlow.typeCheck(this.val); 1972 for (var i = 0; i < len; i++) { 1973 this.caseList.members[i] = typeFlow.typeCheck(this.caseList.members[i]); 1974 } 1975 this.defaultCase = <CaseStatement>typeFlow.typeCheck(this.defaultCase); 1976 this.type = typeFlow.voidType; 1977 return this; 1978 } 1979 1980 // if there are break statements that match this switch, then just link cond block with block after switch 1981 public addToControlFlow(context: ControlFlowContext) { 1982 var condBlock = context.current; 1983 context.addContent(this.val); 1984 var execBlock = new BasicBlock(); 1985 var afterSwitch = new BasicBlock(); 1986 1987 condBlock.addSuccessor(execBlock); 1988 context.pushSwitch(execBlock); 1989 context.current = execBlock; 1990 context.pushStatement(this, execBlock, afterSwitch); 1991 context.walk(this.caseList, this); 1992 context.popSwitch(); 1993 var targetInfo = context.popStatement(); 1994 var hasCondContinuation = (this.defaultCase == null); 1995 if (this.defaultCase == null) { 1996 condBlock.addSuccessor(afterSwitch); 1997 } 1998 if (afterSwitch.predecessors.length > 0) { 1999 context.noContinuation = false; 2000 context.current = afterSwitch; 2001 } 2002 else { 2003 context.noContinuation = true; 2004 } 2005 context.walker.options.goChildren = false; 2006 } 2007 } 2008 2009 export class CaseStatement extends Statement { 2010 public expr: AST = null; 2011 public body: ASTList; 2012 2013 constructor () { 2014 super(NodeType.Case); 2015 } 2016 2017 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2018 emitter.emitParensAndCommentsInPlace(this, true); 2019 emitter.recordSourceMappingStart(this); 2020 if (this.expr) { 2021 emitter.writeToOutput("case "); 2022 emitter.emitJavascript(this.expr, TokenID.Identifier, false); 2023 } 2024 else { 2025 emitter.writeToOutput("default"); 2026 } 2027 emitter.writeToOutput(":"); 2028 emitter.emitJavascriptStatements(this.body, false, false); 2029 emitter.recordSourceMappingEnd(this); 2030 emitter.emitParensAndCommentsInPlace(this, false); 2031 } 2032 2033 public typeCheck(typeFlow: TypeFlow) { 2034 this.expr = typeFlow.typeCheck(this.expr); 2035 typeFlow.typeCheck(this.body); 2036 this.type = typeFlow.voidType; 2037 return this; 2038 } 2039 2040 // TODO: more reasoning about unreachable cases (such as duplicate literals as case expressions) 2041 // for now, assume all cases are reachable, regardless of whether some cases fall through 2042 public addToControlFlow(context: ControlFlowContext) { 2043 var execBlock = new BasicBlock(); 2044 var sw = context.currentSwitch[context.currentSwitch.length - 1]; 2045 // TODO: fall-through from previous (+ to end of switch) 2046 if (this.expr) { 2047 var exprBlock = new BasicBlock(); 2048 context.current = exprBlock; 2049 sw.addSuccessor(exprBlock); 2050 context.addContent(this.expr); 2051 exprBlock.addSuccessor(execBlock); 2052 } 2053 else { 2054 sw.addSuccessor(execBlock); 2055 } 2056 context.current = execBlock; 2057 if (this.body) { 2058 context.walk(this.body, this); 2059 } 2060 context.noContinuation = false; 2061 context.walker.options.goChildren = false; 2062 } 2063 } 2064 2065 export class TypeReference extends AST { 2066 constructor (public term: AST, public arrayCount: number) { 2067 super(NodeType.TypeRef); 2068 } 2069 2070 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2071 throw new Error("should not emit a type ref"); 2072 } 2073 2074 public typeCheck(typeFlow: TypeFlow) { 2075 var prevInTCTR = typeFlow.inTypeRefTypeCheck; 2076 typeFlow.inTypeRefTypeCheck = true; 2077 var typeLink = getTypeLink(this, typeFlow.checker, true); 2078 typeFlow.checker.resolveTypeLink(typeFlow.scope, typeLink, false); 2079 2080 if (this.term) { 2081 typeFlow.typeCheck(this.term); 2082 } 2083 2084 typeFlow.checkForVoidConstructor(typeLink.type, this); 2085 2086 this.type = typeLink.type; 2087 2088 // in error recovery cases, there may not be a term 2089 if (this.term) { 2090 this.term.type = this.type; 2091 } 2092 2093 typeFlow.inTypeRefTypeCheck = prevInTCTR; 2094 return this; 2095 } 2096 } 2097 2098 export class TryFinally extends Statement { 2099 constructor (public tryNode: AST, public finallyNode: Finally) { 2100 super(NodeType.TryFinally); 2101 } 2102 2103 public isCompoundStatement() { return true; } 2104 2105 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2106 emitter.recordSourceMappingStart(this); 2107 emitter.emitJavascript(this.tryNode, TokenID.Try, false); 2108 emitter.emitJavascript(this.finallyNode, TokenID.Finally, false); 2109 emitter.recordSourceMappingEnd(this); 2110 } 2111 2112 public typeCheck(typeFlow: TypeFlow) { 2113 this.tryNode = typeFlow.typeCheck(this.tryNode); 2114 this.finallyNode = <Finally>typeFlow.typeCheck(this.finallyNode); 2115 this.type = typeFlow.voidType; 2116 return this; 2117 } 2118 2119 public addToControlFlow(context: ControlFlowContext) { 2120 var afterFinally = new BasicBlock(); 2121 context.walk(this.tryNode, this); 2122 var finBlock = new BasicBlock(); 2123 if (context.current) { 2124 context.current.addSuccessor(finBlock); 2125 } 2126 context.current = finBlock; 2127 context.pushStatement(this, null, afterFinally); 2128 context.walk(this.finallyNode, this); 2129 if (!context.noContinuation && context.current) { 2130 context.current.addSuccessor(afterFinally); 2131 } 2132 if (afterFinally.predecessors.length > 0) { 2133 context.current = afterFinally; 2134 } 2135 else { 2136 context.noContinuation = true; 2137 } 2138 context.popStatement(); 2139 context.walker.options.goChildren = false; 2140 } 2141 } 2142 2143 export class TryCatch extends Statement { 2144 constructor (public tryNode: Try, public catchNode: Catch) { 2145 super(NodeType.TryCatch); 2146 } 2147 2148 public isCompoundStatement() { return true; } 2149 2150 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2151 emitter.emitParensAndCommentsInPlace(this, true); 2152 emitter.recordSourceMappingStart(this); 2153 emitter.emitJavascript(this.tryNode, TokenID.Try, false); 2154 emitter.emitJavascript(this.catchNode, TokenID.Catch, false); 2155 emitter.recordSourceMappingEnd(this); 2156 emitter.emitParensAndCommentsInPlace(this, false); 2157 } 2158 2159 public addToControlFlow(context: ControlFlowContext) { 2160 var beforeTry = context.current; 2161 var tryBlock = new BasicBlock(); 2162 beforeTry.addSuccessor(tryBlock); 2163 context.current = tryBlock; 2164 var afterTryCatch = new BasicBlock(); 2165 context.pushStatement(this, null, afterTryCatch); 2166 context.walk(this.tryNode, this); 2167 if (!context.noContinuation) { 2168 if (context.current) { 2169 context.current.addSuccessor(afterTryCatch); 2170 } 2171 } 2172 context.current = new BasicBlock(); 2173 beforeTry.addSuccessor(context.current); 2174 context.walk(this.catchNode, this); 2175 context.popStatement(); 2176 if (!context.noContinuation) { 2177 if (context.current) { 2178 context.current.addSuccessor(afterTryCatch); 2179 } 2180 } 2181 context.current = afterTryCatch; 2182 context.walker.options.goChildren = false; 2183 } 2184 2185 public typeCheck(typeFlow: TypeFlow) { 2186 this.tryNode = <Try>typeFlow.typeCheck(this.tryNode); 2187 this.catchNode = <Catch>typeFlow.typeCheck(this.catchNode); 2188 this.type = typeFlow.voidType; 2189 return this; 2190 } 2191 } 2192 2193 export class Try extends Statement { 2194 constructor (public body: AST) { 2195 super(NodeType.Try); 2196 } 2197 2198 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2199 emitter.emitParensAndCommentsInPlace(this, true); 2200 emitter.recordSourceMappingStart(this); 2201 emitter.writeToOutput("try "); 2202 emitter.emitJavascript(this.body, TokenID.Try, false); 2203 emitter.recordSourceMappingEnd(this); 2204 emitter.emitParensAndCommentsInPlace(this, false); 2205 } 2206 2207 public typeCheck(typeFlow: TypeFlow) { 2208 this.body = typeFlow.typeCheck(this.body); 2209 return this; 2210 } 2211 2212 public addToControlFlow(context: ControlFlowContext) { 2213 if (this.body) { 2214 context.walk(this.body, this); 2215 } 2216 context.walker.options.goChildren = false; 2217 context.noContinuation = false; 2218 } 2219 } 2220 2221 export class Catch extends Statement { 2222 constructor (public param: VarDecl, public body: AST) { 2223 super(NodeType.Catch); 2224 if (this.param) { 2225 this.param.varFlags |= VarFlags.AutoInit; 2226 } 2227 } 2228 public statement: ASTSpan = new ASTSpan(); 2229 public containedScope: SymbolScope = null; 2230 2231 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2232 emitter.emitParensAndCommentsInPlace(this, true); 2233 emitter.recordSourceMappingStart(this); 2234 emitter.writeToOutput(" "); 2235 emitter.recordSourceMappingStart(this.statement); 2236 emitter.writeToOutput("catch ("); 2237 emitter.emitJavascript(this.param, TokenID.OpenParen, false); 2238 emitter.writeToOutput(")"); 2239 emitter.recordSourceMappingEnd(this.statement); 2240 emitter.emitJavascript(this.body, TokenID.Catch, false); 2241 emitter.recordSourceMappingEnd(this); 2242 emitter.emitParensAndCommentsInPlace(this, false); 2243 } 2244 2245 public addToControlFlow(context: ControlFlowContext) { 2246 if (this.param) { 2247 context.addContent(this.param); 2248 var bodBlock = new BasicBlock(); 2249 context.current.addSuccessor(bodBlock); 2250 context.current = bodBlock; 2251 } 2252 if (this.body) { 2253 context.walk(this.body, this); 2254 } 2255 context.noContinuation = false; 2256 context.walker.options.goChildren = false; 2257 } 2258 2259 public typeCheck(typeFlow: TypeFlow) { 2260 var prevScope = typeFlow.scope; 2261 typeFlow.scope = this.containedScope; 2262 this.param = <VarDecl>typeFlow.typeCheck(this.param); 2263 var exceptVar = new ValueLocation(); 2264 var varSym = new VariableSymbol((<VarDecl>this.param).id.text, 2265 this.param.minChar, 2266 typeFlow.checker.locationInfo.unitIndex, 2267 exceptVar); 2268 exceptVar.symbol = varSym; 2269 exceptVar.typeLink = new TypeLink(); 2270 // var type for now (add syntax for type annotation) 2271 exceptVar.typeLink.type = typeFlow.anyType; 2272 var thisFnc = typeFlow.thisFnc; 2273 if (thisFnc && thisFnc.type) { 2274 exceptVar.symbol.container = thisFnc.type.symbol; 2275 } 2276 else { 2277 exceptVar.symbol.container = null; 2278 } 2279 this.param.sym = exceptVar.symbol; 2280 typeFlow.scope.enter(exceptVar.symbol.container, this.param, exceptVar.symbol, 2281 typeFlow.checker.errorReporter, false, false, false); 2282 this.body = typeFlow.typeCheck(this.body); 2283 2284 // if we're in provisional typecheck mode, clean up the symbol entry 2285 // REVIEW: This is obviously bad form, since we're counting on the internal 2286 // layout of the symbol table, but this is also the only place where we insert 2287 // symbols during typecheck 2288 if (typeFlow.checker.inProvisionalTypecheckMode()) { 2289 var table = typeFlow.scope.getTable(); 2290 (<any>table).secondaryTable.table[exceptVar.symbol.name] = undefined; 2291 } 2292 this.type = typeFlow.voidType; 2293 typeFlow.scope = prevScope; 2294 return this; 2295 } 2296 } 2297 2298 export class Finally extends Statement { 2299 constructor (public body: AST) { 2300 super(NodeType.Finally); 2301 } 2302 2303 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2304 emitter.emitParensAndCommentsInPlace(this, true); 2305 emitter.recordSourceMappingStart(this); 2306 emitter.writeToOutput("finally"); 2307 emitter.emitJavascript(this.body, TokenID.Finally, false); 2308 emitter.recordSourceMappingEnd(this); 2309 emitter.emitParensAndCommentsInPlace(this, false); 2310 } 2311 2312 public addToControlFlow(context: ControlFlowContext) { 2313 if (this.body) { 2314 context.walk(this.body, this); 2315 } 2316 context.walker.options.goChildren = false; 2317 context.noContinuation = false; 2318 } 2319 2320 public typeCheck(typeFlow: TypeFlow) { 2321 this.body = typeFlow.typeCheck(this.body); 2322 return this; 2323 } 2324 } 2325 2326 export class Comment extends AST { 2327 2328 public text: string[] = null; 2329 2330 constructor (public content: string, public isBlockComment: boolean, public endsLine) { 2331 super(NodeType.Comment); 2332 } 2333 2334 public getText(): string[] { 2335 if (this.text == null) { 2336 if (this.isBlockComment) { 2337 this.text = this.content.split("\n"); 2338 for (var i = 0; i < this.text.length; i++) { 2339 this.text[i] = this.text[i].replace(/^\s+|\s+$/g, ''); 2340 } 2341 } 2342 else { 2343 this.text = [(this.content.replace(/^\s+|\s+$/g, ''))]; 2344 } 2345 } 2346 2347 return this.text; 2348 } 2349 } 2350 2351 export class DebuggerStatement extends Statement { 2352 constructor () { 2353 super(NodeType.Debugger); 2354 } 2355 2356 public emit(emitter: Emitter, tokenId: TokenID, startLine: boolean) { 2357 emitter.emitParensAndCommentsInPlace(this, true); 2358 emitter.recordSourceMappingStart(this); 2359 emitter.writeLineToOutput("debugger;"); 2360 emitter.recordSourceMappingEnd(this); 2361 emitter.emitParensAndCommentsInPlace(this, false); 2362 } 2363 } 2364}