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 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.shouldEmitTypes: 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 12 matching lines...) Expand all Loading... |
44 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { | 46 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { |
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 // TODO(ochafik): Inline the body of [TypeScriptTypePrinter] here if/when it no |
55 class Printer implements NodeVisitor { | 57 // longer needs to share utils with [ClosureTypePrinter]. |
| 58 class Printer extends TypeScriptTypePrinter implements NodeVisitor { |
56 final JavaScriptPrintingOptions options; | 59 final JavaScriptPrintingOptions options; |
57 final JavaScriptPrintingContext context; | 60 final JavaScriptPrintingContext context; |
58 final bool shouldCompressOutput; | 61 final bool shouldCompressOutput; |
59 final DanglingElseVisitor danglingElseVisitor; | 62 final DanglingElseVisitor danglingElseVisitor; |
60 final LocalNamer localNamer; | 63 final LocalNamer localNamer; |
61 | 64 |
62 bool inForInit = false; | 65 bool inForInit = false; |
63 bool atStatementBegin = false; | 66 bool atStatementBegin = false; |
64 bool inNewTarget = false; | 67 bool inNewTarget = false; |
65 bool pendingSemicolon = false; | 68 bool pendingSemicolon = false; |
(...skipping 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 void functionOut(Fun fun, Node name) { | 537 void functionOut(Fun fun, Node name) { |
535 out("function"); | 538 out("function"); |
536 if (fun.isGenerator) out("*"); | 539 if (fun.isGenerator) out("*"); |
537 if (name != null) { | 540 if (name != null) { |
538 out(" "); | 541 out(" "); |
539 // Name must be a [Decl]. Therefore only test for primary expressions. | 542 // Name must be a [Decl]. Therefore only test for primary expressions. |
540 visitNestedExpression(name, PRIMARY, | 543 visitNestedExpression(name, PRIMARY, |
541 newInForInit: false, newAtStatementBegin: false); | 544 newInForInit: false, newAtStatementBegin: false); |
542 } | 545 } |
543 localNamer.enterScope(fun); | 546 localNamer.enterScope(fun); |
| 547 outTypeParams(fun.typeParams); |
544 out("("); | 548 out("("); |
545 if (fun.params != null) { | 549 if (fun.params != null) { |
546 visitCommaSeparated(fun.params, PRIMARY, | 550 visitCommaSeparated(fun.params, PRIMARY, |
547 newInForInit: false, newAtStatementBegin: false); | 551 newInForInit: false, newAtStatementBegin: false); |
548 } | 552 } |
549 out(")"); | 553 out(")"); |
| 554 outTypeAnnotation(fun.returnType); |
550 switch (fun.asyncModifier) { | 555 switch (fun.asyncModifier) { |
551 case const AsyncModifier.sync(): | 556 case const AsyncModifier.sync(): |
552 break; | 557 break; |
553 case const AsyncModifier.async(): | 558 case const AsyncModifier.async(): |
554 out(' async'); | 559 out(' async'); |
555 break; | 560 break; |
556 case const AsyncModifier.syncStar(): | 561 case const AsyncModifier.syncStar(): |
557 out(' sync*'); | 562 out(' sync*'); |
558 break; | 563 break; |
559 case const AsyncModifier.asyncStar(): | 564 case const AsyncModifier.asyncStar(): |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
594 out(")"); | 599 out(")"); |
595 } else { | 600 } else { |
596 inForInit = newInForInit; | 601 inForInit = newInForInit; |
597 atStatementBegin = newAtStatementBegin; | 602 atStatementBegin = newAtStatementBegin; |
598 visit(node); | 603 visit(node); |
599 } | 604 } |
600 } | 605 } |
601 | 606 |
602 visitVariableDeclarationList(VariableDeclarationList list) { | 607 visitVariableDeclarationList(VariableDeclarationList list) { |
603 outClosureAnnotation(list); | 608 outClosureAnnotation(list); |
604 out(list.keyword); | 609 // Note: keyword can be null for non-static field declarations. |
605 out(" "); | 610 if (list.keyword != null) { |
| 611 out(list.keyword); |
| 612 out(" "); |
| 613 } |
606 visitCommaSeparated(list.declarations, ASSIGNMENT, | 614 visitCommaSeparated(list.declarations, ASSIGNMENT, |
607 newInForInit: inForInit, newAtStatementBegin: false); | 615 newInForInit: inForInit, newAtStatementBegin: false); |
608 } | 616 } |
609 | 617 |
610 visitArrayBindingPattern(ArrayBindingPattern node) { | 618 visitArrayBindingPattern(ArrayBindingPattern node) { |
611 out("["); | 619 out("["); |
612 visitCommaSeparated(node.variables, EXPRESSION, | 620 visitCommaSeparated(node.variables, EXPRESSION, |
613 newInForInit: false, newAtStatementBegin: false); | 621 newInForInit: false, newAtStatementBegin: false); |
614 out("]"); | 622 out("]"); |
615 } | 623 } |
(...skipping 16 matching lines...) Expand all Loading... |
632 visit(name); | 640 visit(name); |
633 } | 641 } |
634 } | 642 } |
635 if (node.structure != null) { | 643 if (node.structure != null) { |
636 if (hasName) { | 644 if (hasName) { |
637 out(":"); | 645 out(":"); |
638 spaceOut(); | 646 spaceOut(); |
639 } | 647 } |
640 visit(node.structure); | 648 visit(node.structure); |
641 } | 649 } |
| 650 outTypeAnnotation(node.type); |
642 if (node.defaultValue != null) { | 651 if (node.defaultValue != null) { |
643 spaceOut(); | 652 spaceOut(); |
644 out("="); | 653 out("="); |
645 spaceOut(); | 654 spaceOut(); |
646 visitNestedExpression(node.defaultValue, EXPRESSION, | 655 visitNestedExpression(node.defaultValue, EXPRESSION, |
647 newInForInit: false, newAtStatementBegin: false); | 656 newInForInit: false, newAtStatementBegin: false); |
648 } | 657 } |
649 } | 658 } |
650 | 659 |
651 visitSimpleBindingPattern(SimpleBindingPattern node) { | 660 visitSimpleBindingPattern(SimpleBindingPattern node) { |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
862 visitThis(This node) { | 871 visitThis(This node) { |
863 out("this"); | 872 out("this"); |
864 } | 873 } |
865 | 874 |
866 visitSuper(Super node) { | 875 visitSuper(Super node) { |
867 out("super"); | 876 out("super"); |
868 } | 877 } |
869 | 878 |
870 visitIdentifier(Identifier node) { | 879 visitIdentifier(Identifier node) { |
871 out(localNamer.getName(node)); | 880 out(localNamer.getName(node)); |
| 881 outTypeAnnotation(node.type); |
872 } | 882 } |
873 | 883 |
874 visitRestParameter(RestParameter node) { | 884 visitRestParameter(RestParameter node) { |
875 out('...'); | 885 out('...'); |
876 visitIdentifier(node.parameter); | 886 visitIdentifier(node.parameter); |
877 } | 887 } |
878 | 888 |
879 bool isDigit(int charCode) { | 889 bool isDigit(int charCode) { |
880 return charCodes.$0 <= charCode && charCode <= charCodes.$9; | 890 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
881 } | 891 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 visitNamedFunction(NamedFunction namedFunction) { | 937 visitNamedFunction(NamedFunction namedFunction) { |
928 functionOut(namedFunction.function, namedFunction.name); | 938 functionOut(namedFunction.function, namedFunction.name); |
929 } | 939 } |
930 | 940 |
931 visitFun(Fun fun) { | 941 visitFun(Fun fun) { |
932 functionOut(fun, null); | 942 functionOut(fun, null); |
933 } | 943 } |
934 | 944 |
935 visitArrowFun(ArrowFun fun) { | 945 visitArrowFun(ArrowFun fun) { |
936 localNamer.enterScope(fun); | 946 localNamer.enterScope(fun); |
937 if (fun.params.length == 1) { | 947 if (fun.params.length == 1 && |
| 948 (fun.params.single.type == null || !options.shouldEmitTypes)) { |
938 visitNestedExpression(fun.params.single, SPREAD, | 949 visitNestedExpression(fun.params.single, SPREAD, |
939 newInForInit: false, newAtStatementBegin: false); | 950 newInForInit: false, newAtStatementBegin: false); |
940 } else { | 951 } else { |
941 out("("); | 952 out("("); |
942 visitCommaSeparated(fun.params, SPREAD, | 953 visitCommaSeparated(fun.params, SPREAD, |
943 newInForInit: false, newAtStatementBegin: false); | 954 newInForInit: false, newAtStatementBegin: false); |
944 out(")"); | 955 out(")"); |
945 } | 956 } |
| 957 outTypeAnnotation(fun.returnType); |
946 spaceOut(); | 958 spaceOut(); |
947 out("=>"); | 959 out("=>"); |
948 if (fun.body is Expression) { | 960 if (fun.body is Expression) { |
949 spaceOut(); | 961 spaceOut(); |
950 // Object initializers require parenthesis to disambiguate | 962 // Object initializers require parenthesis to disambiguate |
951 // AssignmentExpression from FunctionBody. See: | 963 // AssignmentExpression from FunctionBody. See: |
952 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-functio
n-definitions | 964 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-arrow-functio
n-definitions |
953 var needsParen = fun.body is ObjectInitializer; | 965 var needsParen = fun.body is ObjectInitializer; |
954 if (needsParen) out("("); | 966 if (needsParen) out("("); |
955 visitNestedExpression(fun.body, ASSIGNMENT, | 967 visitNestedExpression(fun.body, ASSIGNMENT, |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1074 visit(node.tag); | 1086 visit(node.tag); |
1075 visit(node.template); | 1087 visit(node.template); |
1076 } | 1088 } |
1077 | 1089 |
1078 visitClassDeclaration(ClassDeclaration node) { | 1090 visitClassDeclaration(ClassDeclaration node) { |
1079 indent(); | 1091 indent(); |
1080 visit(node.classExpr); | 1092 visit(node.classExpr); |
1081 lineOut(); | 1093 lineOut(); |
1082 } | 1094 } |
1083 | 1095 |
| 1096 void outTypeParams(Iterable<Identifier> typeParams) { |
| 1097 if (typeParams != null && options.shouldEmitTypes && typeParams.isNotEmpty)
{ |
| 1098 out("<"); |
| 1099 var first = true; |
| 1100 for (var typeParam in typeParams) { |
| 1101 if (!first) out(", "); |
| 1102 first = false; |
| 1103 visit(typeParam); |
| 1104 } |
| 1105 out(">"); |
| 1106 } |
| 1107 } |
| 1108 |
1084 visitClassExpression(ClassExpression node) { | 1109 visitClassExpression(ClassExpression node) { |
1085 out('class '); | 1110 out('class '); |
1086 visit(node.name); | 1111 visit(node.name); |
| 1112 outTypeParams(node.typeParams); |
1087 if (node.heritage != null) { | 1113 if (node.heritage != null) { |
1088 out(' extends '); | 1114 out(' extends '); |
1089 visit(node.heritage); | 1115 visit(node.heritage); |
1090 } | 1116 } |
1091 spaceOut(); | 1117 spaceOut(); |
1092 if (node.methods.isNotEmpty) { | 1118 if (node.methods.isNotEmpty) { |
1093 out('{'); | 1119 out('{'); |
1094 lineOut(); | 1120 lineOut(); |
1095 indentMore(); | 1121 indentMore(); |
| 1122 if (options.shouldEmitTypes && node.fields != null) { |
| 1123 for (var field in node.fields) { |
| 1124 indent(); |
| 1125 visit(field); |
| 1126 out(";"); |
| 1127 lineOut(); |
| 1128 } |
| 1129 } |
1096 for (var method in node.methods) { | 1130 for (var method in node.methods) { |
1097 indent(); | 1131 indent(); |
1098 visit(method); | 1132 visit(method); |
1099 lineOut(); | 1133 lineOut(); |
1100 } | 1134 } |
1101 indentLess(); | 1135 indentLess(); |
1102 indent(); | 1136 indent(); |
1103 out('}'); | 1137 out('}'); |
1104 } else { | 1138 } else { |
1105 out('{}'); | 1139 out('{}'); |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1317 } else { | 1351 } else { |
1318 out('/* $comment */'); | 1352 out('/* $comment */'); |
1319 } | 1353 } |
1320 visit(node.expression); | 1354 visit(node.expression); |
1321 } | 1355 } |
1322 | 1356 |
1323 void visitAwait(Await node) { | 1357 void visitAwait(Await node) { |
1324 out("await "); | 1358 out("await "); |
1325 visit(node.expression); | 1359 visit(node.expression); |
1326 } | 1360 } |
| 1361 |
| 1362 void outTypeAnnotation(TypeRef node) { |
| 1363 if (node == null || !options.shouldEmitTypes || node.isUnknown) return; |
| 1364 |
| 1365 if (node is OptionalTypeRef) { |
| 1366 out("?: "); |
| 1367 visit(node.type); |
| 1368 } else { |
| 1369 out(": "); |
| 1370 visit(node); |
| 1371 } |
| 1372 } |
1327 } | 1373 } |
1328 | 1374 |
1329 // Collects all the var declarations in the function. We need to do this in a | 1375 // Collects all the var declarations in the function. We need to do this in a |
1330 // separate pass because JS vars are lifted to the top of the function. | 1376 // separate pass because JS vars are lifted to the top of the function. |
1331 class VarCollector extends BaseVisitor { | 1377 class VarCollector extends BaseVisitor { |
1332 bool nested; | 1378 bool nested; |
1333 final Set<String> vars; | 1379 final Set<String> vars; |
1334 final Set<String> params; | 1380 final Set<String> params; |
1335 | 1381 |
1336 VarCollector() : nested = false, | 1382 VarCollector() : nested = false, |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1636 declare(node.name); | 1682 declare(node.name); |
1637 node.function.accept(this); | 1683 node.function.accept(this); |
1638 } | 1684 } |
1639 | 1685 |
1640 visitClassExpression(ClassExpression node) { | 1686 visitClassExpression(ClassExpression node) { |
1641 declare(node.name); | 1687 declare(node.name); |
1642 if (node.heritage != null) node.heritage.accept(this); | 1688 if (node.heritage != null) node.heritage.accept(this); |
1643 for (Method element in node.methods) element.accept(this); | 1689 for (Method element in node.methods) element.accept(this); |
1644 } | 1690 } |
1645 } | 1691 } |
OLD | NEW |