• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}