OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of js_ast; | 5 part of js_ast; |
6 | 6 |
7 | 7 |
8 class JavaScriptPrintingOptions { | 8 class JavaScriptPrintingOptions { |
9 final bool shouldCompressOutput; | 9 final bool shouldCompressOutput; |
10 final bool minifyLocalVariables; | 10 final bool minifyLocalVariables; |
11 final bool preferSemicolonToNewlineInMinifiedOutput; | 11 final bool preferSemicolonToNewlineInMinifiedOutput; |
12 final bool printTypes; | |
Jennifer Messerly
2016/02/09 01:21:33
this sounds like a method name. Maybe "shouldPrint
ochafik
2016/02/10 18:12:45
Thanks, went for shouldEmitTypes
| |
12 final bool allowSingleLineIfStatements; | 13 final bool allowSingleLineIfStatements; |
13 | 14 |
14 /// True to allow keywords in properties, such as `obj.var` or `obj.function` | 15 /// True to allow keywords in properties, such as `obj.var` or `obj.function` |
15 /// Modern JS engines support this. | 16 /// Modern JS engines support this. |
16 final bool allowKeywordsInProperties; | 17 final bool allowKeywordsInProperties; |
17 | 18 |
18 JavaScriptPrintingOptions( | 19 JavaScriptPrintingOptions( |
19 {this.shouldCompressOutput: false, | 20 {this.shouldCompressOutput: false, |
20 this.minifyLocalVariables: false, | 21 this.minifyLocalVariables: false, |
21 this.preferSemicolonToNewlineInMinifiedOutput: false, | 22 this.preferSemicolonToNewlineInMinifiedOutput: false, |
23 this.printTypes: false, | |
22 this.allowKeywordsInProperties: false, | 24 this.allowKeywordsInProperties: false, |
23 this.allowSingleLineIfStatements: false}); | 25 this.allowSingleLineIfStatements: false}); |
24 } | 26 } |
25 | 27 |
26 | 28 |
27 /// An environment in which JavaScript printing is done. Provides emitting of | 29 /// An environment in which JavaScript printing is done. Provides emitting of |
28 /// text and pre- and post-visit callbacks. | 30 /// text and pre- and post-visit callbacks. |
29 abstract class JavaScriptPrintingContext { | 31 abstract class JavaScriptPrintingContext { |
30 /// Signals an error. This should happen only for serious internal errors. | 32 /// Signals an error. This should happen only for serious internal errors. |
31 void error(String message) { throw message; } | 33 void error(String message) { throw message; } |
(...skipping 13 matching lines...) Expand all Loading... | |
45 final StringBuffer buffer = new StringBuffer(); | 47 final StringBuffer buffer = new StringBuffer(); |
46 | 48 |
47 void emit(String string) { | 49 void emit(String string) { |
48 buffer.write(string); | 50 buffer.write(string); |
49 } | 51 } |
50 | 52 |
51 String getText() => buffer.toString(); | 53 String getText() => buffer.toString(); |
52 } | 54 } |
53 | 55 |
54 | 56 |
55 class Printer implements NodeVisitor { | 57 class Printer extends TypeScriptTypePrinter implements NodeVisitor { |
Jennifer Messerly
2016/02/09 01:21:34
Hmmm. I haven't seen the other class yet, but this
ochafik
2016/02/10 18:12:45
Good point, left a TODO for when/if we drop Closur
| |
56 final JavaScriptPrintingOptions options; | 58 final JavaScriptPrintingOptions options; |
57 final JavaScriptPrintingContext context; | 59 final JavaScriptPrintingContext context; |
58 final bool shouldCompressOutput; | 60 final bool shouldCompressOutput; |
59 final DanglingElseVisitor danglingElseVisitor; | 61 final DanglingElseVisitor danglingElseVisitor; |
60 final LocalNamer localNamer; | 62 final LocalNamer localNamer; |
61 | 63 |
62 bool inForInit = false; | 64 bool inForInit = false; |
63 bool atStatementBegin = false; | 65 bool atStatementBegin = false; |
64 bool inNewTarget = false; | 66 bool inNewTarget = false; |
65 bool pendingSemicolon = false; | 67 bool pendingSemicolon = false; |
(...skipping 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
534 void functionOut(Fun fun, Node name) { | 536 void functionOut(Fun fun, Node name) { |
535 out("function"); | 537 out("function"); |
536 if (fun.isGenerator) out("*"); | 538 if (fun.isGenerator) out("*"); |
537 if (name != null) { | 539 if (name != null) { |
538 out(" "); | 540 out(" "); |
539 // Name must be a [Decl]. Therefore only test for primary expressions. | 541 // Name must be a [Decl]. Therefore only test for primary expressions. |
540 visitNestedExpression(name, PRIMARY, | 542 visitNestedExpression(name, PRIMARY, |
541 newInForInit: false, newAtStatementBegin: false); | 543 newInForInit: false, newAtStatementBegin: false); |
542 } | 544 } |
543 localNamer.enterScope(fun); | 545 localNamer.enterScope(fun); |
546 outTypeArgs(fun.typeArgs); | |
544 out("("); | 547 out("("); |
545 if (fun.params != null) { | 548 if (fun.params != null) { |
546 visitCommaSeparated(fun.params, PRIMARY, | 549 visitCommaSeparated(fun.params, PRIMARY, |
547 newInForInit: false, newAtStatementBegin: false); | 550 newInForInit: false, newAtStatementBegin: false); |
548 } | 551 } |
549 out(")"); | 552 out(")"); |
553 outTypeAnnotation(fun.returnType); | |
550 switch (fun.asyncModifier) { | 554 switch (fun.asyncModifier) { |
551 case const AsyncModifier.sync(): | 555 case const AsyncModifier.sync(): |
552 break; | 556 break; |
553 case const AsyncModifier.async(): | 557 case const AsyncModifier.async(): |
554 out(' async'); | 558 out(' async'); |
555 break; | 559 break; |
556 case const AsyncModifier.syncStar(): | 560 case const AsyncModifier.syncStar(): |
557 out(' sync*'); | 561 out(' sync*'); |
558 break; | 562 break; |
559 case const AsyncModifier.asyncStar(): | 563 case const AsyncModifier.asyncStar(): |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
594 out(")"); | 598 out(")"); |
595 } else { | 599 } else { |
596 inForInit = newInForInit; | 600 inForInit = newInForInit; |
597 atStatementBegin = newAtStatementBegin; | 601 atStatementBegin = newAtStatementBegin; |
598 visit(node); | 602 visit(node); |
599 } | 603 } |
600 } | 604 } |
601 | 605 |
602 visitVariableDeclarationList(VariableDeclarationList list) { | 606 visitVariableDeclarationList(VariableDeclarationList list) { |
603 outClosureAnnotation(list); | 607 outClosureAnnotation(list); |
604 out(list.keyword); | 608 // Note: keyword can be null for non-static field declarations. |
605 out(" "); | 609 if (list.keyword != null) { |
610 out(list.keyword); | |
611 out(" "); | |
612 } | |
606 visitCommaSeparated(list.declarations, ASSIGNMENT, | 613 visitCommaSeparated(list.declarations, ASSIGNMENT, |
607 newInForInit: inForInit, newAtStatementBegin: false); | 614 newInForInit: inForInit, newAtStatementBegin: false); |
608 } | 615 } |
609 | 616 |
610 visitArrayBindingPattern(ArrayBindingPattern node) { | 617 visitArrayBindingPattern(ArrayBindingPattern node) { |
611 out("["); | 618 out("["); |
612 visitCommaSeparated(node.variables, EXPRESSION, | 619 visitCommaSeparated(node.variables, EXPRESSION, |
613 newInForInit: false, newAtStatementBegin: false); | 620 newInForInit: false, newAtStatementBegin: false); |
614 out("]"); | 621 out("]"); |
615 } | 622 } |
616 visitObjectBindingPattern(ObjectBindingPattern node) { | 623 visitObjectBindingPattern(ObjectBindingPattern node) { |
617 out("{"); | 624 out("{"); |
618 visitCommaSeparated(node.variables, EXPRESSION, | 625 visitCommaSeparated(node.variables, EXPRESSION, |
619 newInForInit: false, newAtStatementBegin: false); | 626 newInForInit: false, newAtStatementBegin: false); |
620 out("}"); | 627 out("}"); |
621 } | 628 } |
622 | 629 |
623 visitDestructuredVariable(DestructuredVariable node) { | 630 visitDestructuredVariable(DestructuredVariable node) { |
624 var hasName = node.name != null; | 631 var hasName = node.name != null; |
625 if (hasName) visit(node.name); | 632 if (hasName) visit(node.name); |
626 if (node.structure != null) { | 633 if (node.structure != null) { |
627 if (hasName) { | 634 if (hasName) { |
628 out(":"); | 635 out(":"); |
629 spaceOut(); | 636 spaceOut(); |
630 } | 637 } |
631 visit(node.structure); | 638 visit(node.structure); |
632 } | 639 } |
640 outTypeAnnotation(node.type); | |
633 if (node.defaultValue != null) { | 641 if (node.defaultValue != null) { |
634 spaceOut(); | 642 spaceOut(); |
635 out("="); | 643 out("="); |
636 spaceOut(); | 644 spaceOut(); |
637 visitNestedExpression(node.defaultValue, EXPRESSION, | 645 visitNestedExpression(node.defaultValue, EXPRESSION, |
638 newInForInit: false, newAtStatementBegin: false); | 646 newInForInit: false, newAtStatementBegin: false); |
639 } | 647 } |
640 } | 648 } |
641 | 649 |
642 visitAssignment(Assignment assignment) { | 650 visitAssignment(Assignment assignment) { |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
849 visitThis(This node) { | 857 visitThis(This node) { |
850 out("this"); | 858 out("this"); |
851 } | 859 } |
852 | 860 |
853 visitSuper(Super node) { | 861 visitSuper(Super node) { |
854 out("super"); | 862 out("super"); |
855 } | 863 } |
856 | 864 |
857 visitIdentifier(Identifier node) { | 865 visitIdentifier(Identifier node) { |
858 out(localNamer.getName(node)); | 866 out(localNamer.getName(node)); |
867 outTypeAnnotation(node.type); | |
859 } | 868 } |
860 | 869 |
861 visitRestParameter(RestParameter node) { | 870 visitRestParameter(RestParameter node) { |
862 out('...'); | 871 out('...'); |
863 visitIdentifier(node.parameter); | 872 visitIdentifier(node.parameter); |
864 } | 873 } |
865 | 874 |
866 bool isDigit(int charCode) { | 875 bool isDigit(int charCode) { |
867 return charCodes.$0 <= charCode && charCode <= charCodes.$9; | 876 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
868 } | 877 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
914 visitNamedFunction(NamedFunction namedFunction) { | 923 visitNamedFunction(NamedFunction namedFunction) { |
915 functionOut(namedFunction.function, namedFunction.name); | 924 functionOut(namedFunction.function, namedFunction.name); |
916 } | 925 } |
917 | 926 |
918 visitFun(Fun fun) { | 927 visitFun(Fun fun) { |
919 functionOut(fun, null); | 928 functionOut(fun, null); |
920 } | 929 } |
921 | 930 |
922 visitArrowFun(ArrowFun fun) { | 931 visitArrowFun(ArrowFun fun) { |
923 localNamer.enterScope(fun); | 932 localNamer.enterScope(fun); |
924 if (fun.params.length == 1) { | 933 if (fun.params.length == 1 && |
934 (fun.params.single.type == null || !options.printTypes)) { | |
925 visitNestedExpression(fun.params.single, SPREAD, | 935 visitNestedExpression(fun.params.single, SPREAD, |
926 newInForInit: false, newAtStatementBegin: false); | 936 newInForInit: false, newAtStatementBegin: false); |
927 } else { | 937 } else { |
928 out("("); | 938 out("("); |
929 visitCommaSeparated(fun.params, SPREAD, | 939 visitCommaSeparated(fun.params, SPREAD, |
930 newInForInit: false, newAtStatementBegin: false); | 940 newInForInit: false, newAtStatementBegin: false); |
931 out(")"); | 941 out(")"); |
932 } | 942 } |
943 outTypeAnnotation(fun.returnType); | |
933 spaceOut(); | 944 spaceOut(); |
934 out("=>"); | 945 out("=>"); |
935 if (fun.body is Expression) { | 946 if (fun.body is Expression) { |
936 spaceOut(); | 947 spaceOut(); |
937 // Object initializers require parenthesis to disambiguate | 948 // Object initializers require parenthesis to disambiguate |
938 // AssignmentExpression from FunctionBody. See: | 949 // AssignmentExpression from FunctionBody. See: |
939 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-functio n-definitions | 950 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-functio n-definitions |
940 var needsParen = fun.body is ObjectInitializer; | 951 var needsParen = fun.body is ObjectInitializer; |
941 if (needsParen) out("("); | 952 if (needsParen) out("("); |
942 visitNestedExpression(fun.body, ASSIGNMENT, | 953 visitNestedExpression(fun.body, ASSIGNMENT, |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1061 visit(node.tag); | 1072 visit(node.tag); |
1062 visit(node.template); | 1073 visit(node.template); |
1063 } | 1074 } |
1064 | 1075 |
1065 visitClassDeclaration(ClassDeclaration node) { | 1076 visitClassDeclaration(ClassDeclaration node) { |
1066 indent(); | 1077 indent(); |
1067 visit(node.classExpr); | 1078 visit(node.classExpr); |
1068 lineOut(); | 1079 lineOut(); |
1069 } | 1080 } |
1070 | 1081 |
1082 outTypeArgs(List<Identifier> typeArgs) { | |
1083 if (options.printTypes && typeArgs != null && typeArgs.isNotEmpty) { | |
Jennifer Messerly
2016/02/09 01:21:33
null check on variable/param is probably fastest,
ochafik
2016/02/10 18:12:45
Done.
| |
1084 out("<"); | |
Jennifer Messerly
2016/02/09 01:21:33
does typescript have any cases where precedence co
ochafik
2016/02/10 18:12:45
It *looks* fine in TS (since the : would announce
| |
1085 var first = true; | |
1086 for (var typeParam in typeArgs) { | |
1087 if (!first) out(", "); | |
1088 first = false; | |
1089 visit(typeParam); | |
1090 } | |
1091 out(">"); | |
1092 } | |
1093 } | |
1094 | |
1071 visitClassExpression(ClassExpression node) { | 1095 visitClassExpression(ClassExpression node) { |
1072 out('class '); | 1096 out('class '); |
1073 visit(node.name); | 1097 visit(node.name); |
1098 outTypeArgs(node.typeArgs); | |
1074 if (node.heritage != null) { | 1099 if (node.heritage != null) { |
1075 out(' extends '); | 1100 out(' extends '); |
1076 visit(node.heritage); | 1101 visit(node.heritage); |
1077 } | 1102 } |
1078 spaceOut(); | 1103 spaceOut(); |
1079 if (node.methods.isNotEmpty) { | 1104 if (node.methods.isNotEmpty) { |
1080 out('{'); | 1105 out('{'); |
1081 lineOut(); | 1106 lineOut(); |
1082 indentMore(); | 1107 indentMore(); |
1108 if (options.printTypes) { | |
1109 for (var field in node.fields) { | |
1110 indent(); | |
1111 visit(field); | |
1112 out(";"); | |
1113 lineOut(); | |
1114 } | |
1115 } | |
1083 for (var method in node.methods) { | 1116 for (var method in node.methods) { |
1084 indent(); | 1117 indent(); |
1085 visit(method); | 1118 visit(method); |
1086 lineOut(); | 1119 lineOut(); |
1087 } | 1120 } |
1088 indentLess(); | 1121 indentLess(); |
1089 indent(); | 1122 indent(); |
1090 out('}'); | 1123 out('}'); |
1091 } else { | 1124 } else { |
1092 out('{}'); | 1125 out('{}'); |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1304 } else { | 1337 } else { |
1305 out('/* $comment */'); | 1338 out('/* $comment */'); |
1306 } | 1339 } |
1307 visit(node.expression); | 1340 visit(node.expression); |
1308 } | 1341 } |
1309 | 1342 |
1310 void visitAwait(Await node) { | 1343 void visitAwait(Await node) { |
1311 out("await "); | 1344 out("await "); |
1312 visit(node.expression); | 1345 visit(node.expression); |
1313 } | 1346 } |
1347 | |
1348 void outTypeAnnotation(TypeRef node) { | |
1349 if (!options.printTypes || node == null || node.isUnknown) return; | |
Jennifer Messerly
2016/02/09 01:21:33
silly micro opt: check null first
ochafik
2016/02/10 18:12:45
Done.
| |
1350 | |
1351 if (node is OptionalTypeRef) { | |
1352 out("?: "); | |
1353 visit(node.type); | |
1354 } else { | |
1355 out(": "); | |
1356 visit(node); | |
1357 } | |
1358 } | |
1314 } | 1359 } |
1315 | 1360 |
1316 // Collects all the var declarations in the function. We need to do this in a | 1361 // Collects all the var declarations in the function. We need to do this in a |
1317 // separate pass because JS vars are lifted to the top of the function. | 1362 // separate pass because JS vars are lifted to the top of the function. |
1318 class VarCollector extends BaseVisitor { | 1363 class VarCollector extends BaseVisitor { |
1319 bool nested; | 1364 bool nested; |
1320 final Set<String> vars; | 1365 final Set<String> vars; |
1321 final Set<String> params; | 1366 final Set<String> params; |
1322 | 1367 |
1323 VarCollector() : nested = false, | 1368 VarCollector() : nested = false, |
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1613 declare(node.name); | 1658 declare(node.name); |
1614 node.function.accept(this); | 1659 node.function.accept(this); |
1615 } | 1660 } |
1616 | 1661 |
1617 visitClassExpression(ClassExpression node) { | 1662 visitClassExpression(ClassExpression node) { |
1618 declare(node.name); | 1663 declare(node.name); |
1619 if (node.heritage != null) node.heritage.accept(this); | 1664 if (node.heritage != null) node.heritage.accept(this); |
1620 for (Method element in node.methods) element.accept(this); | 1665 for (Method element in node.methods) element.accept(this); |
1621 } | 1666 } |
1622 } | 1667 } |
OLD | NEW |