@@ -115,9 +115,10 @@ public class JsToStringGenerationVisitor extends JsVisitor {
115115 * How many lines of code to print inside of a JsBlock when printing terse.
116116 */
117117 private static final int JSBLOCK_LINES_TO_PRINT = 3 ;
118+ private static final long MAX_DECIMAL_VALUE = 999_999_999_999L ;
118119
119120 protected boolean needSemi = true ;
120- private List <NamedRange > classRanges = new ArrayList <NamedRange >();
121+ private final List <NamedRange > classRanges = new ArrayList <>();
121122 private NamedRange currentClassRange ;
122123 private NamedRange programClassRange ;
123124
@@ -127,27 +128,39 @@ public class JsToStringGenerationVisitor extends JsVisitor {
127128 * because the statements designated by statementEnds and statementStarts are
128129 * those that appear directly within these global blocks.
129130 */
130- private Set <JsBlock > globalBlocks = new HashSet <JsBlock >();
131+ private final Set <JsBlock > globalBlocks = new HashSet <>();
131132 private final TextOutput p ;
132- private ArrayList <Integer > statementEnds = new ArrayList <Integer >();
133- private ArrayList <Integer > statementStarts = new ArrayList <Integer >();
133+ private final ArrayList <Integer > statementEnds = new ArrayList <>();
134+ private final ArrayList <Integer > statementStarts = new ArrayList <>();
134135 private final boolean useLongIdents ;
136+ private final boolean minifyLiterals ;
137+
138+ public static class PrintOptions {
139+ public final boolean useLongIdents ;
140+ public final boolean minifyLiterals ;
141+
142+ public PrintOptions (boolean useLongIdents , boolean minifyLiterals ) {
143+ this .useLongIdents = useLongIdents ;
144+ this .minifyLiterals = minifyLiterals ;
145+ }
146+ }
135147
136148 /**
137149 * Generate the output string using short identifiers.
138150 */
139151 public JsToStringGenerationVisitor (TextOutput out ) {
140- this (out , false );
152+ this (out , new PrintOptions ( false , false ) );
141153 }
142154
143155 /**
144156 * Generate the output string using short or long identifiers.
145157 *
146- * @param useLongIdents if true, emit all identifiers in long form
158+ * @param options settings for minification
147159 */
148- JsToStringGenerationVisitor (TextOutput out , boolean useLongIdents ) {
160+ JsToStringGenerationVisitor (TextOutput out , PrintOptions options ) {
149161 this .p = out ;
150- this .useLongIdents = useLongIdents ;
162+ this .useLongIdents = options .useLongIdents ;
163+ this .minifyLiterals = options .minifyLiterals ;
151164 }
152165
153166 public List <NamedRange > getClassRanges () {
@@ -183,8 +196,7 @@ public boolean visit(JsArrayAccess x, JsContext ctx) {
183196 public boolean visit (JsArrayLiteral x , JsContext ctx ) {
184197 _lsquare ();
185198 boolean sep = false ;
186- for (Object element : x .getExpressions ()) {
187- JsExpression arg = (JsExpression ) element ;
199+ for (JsExpression arg : x .getExpressions ()) {
188200 sep = _sepCommaOptSpace (sep );
189201 _parenPushIfCommaExpr (arg );
190202 accept (arg );
@@ -227,6 +239,10 @@ public boolean visit(JsBlock x, JsContext ctx) {
227239
228240 @ Override
229241 public boolean visit (JsBooleanLiteral x , JsContext ctx ) {
242+ if (minifyLiterals ) {
243+ p .print (x .getValue () ? "!0" : "!1" );
244+ return false ;
245+ }
230246 if (x .getValue ()) {
231247 _true ();
232248 } else {
@@ -257,8 +273,7 @@ public boolean visit(JsCase x, JsContext ctx) {
257273 _newlineOpt ();
258274
259275 indent ();
260- for (Object element : x .getStmts ()) {
261- JsStatement stmt = (JsStatement ) element ;
276+ for (JsStatement stmt : x .getStmts ()) {
262277 needSemi = true ;
263278 accept (stmt );
264279 if (needSemi ) {
@@ -387,8 +402,7 @@ public boolean visit(JsDefault x, JsContext ctx) {
387402 _colon ();
388403
389404 indent ();
390- for (Object element : x .getStmts ()) {
391- JsStatement stmt = (JsStatement ) element ;
405+ for (JsStatement stmt : x .getStmts ()) {
392406 needSemi = true ;
393407 accept (stmt );
394408 if (needSemi ) {
@@ -532,8 +546,7 @@ public boolean visit(JsFunction x, JsContext ctx) {
532546
533547 _lparen ();
534548 boolean sep = false ;
535- for (Object element : x .getParameters ()) {
536- JsParameter param = (JsParameter ) element ;
549+ for (JsParameter param : x .getParameters ()) {
537550 sep = _sepCommaOptSpace (sep );
538551 accept (param );
539552 }
@@ -588,8 +601,7 @@ public boolean visit(JsInvocation x, JsContext ctx) {
588601
589602 _lparen ();
590603 boolean sep = false ;
591- for (Object element : x .getArguments ()) {
592- JsExpression arg = (JsExpression ) element ;
604+ for (JsExpression arg : x .getArguments ()) {
593605 sep = _sepCommaOptSpace (sep );
594606 _parenPushIfCommaExpr (arg );
595607 accept (arg );
@@ -626,7 +638,7 @@ public boolean visit(JsNameRef x, JsContext ctx) {
626638 _parenPush (x , q , false );
627639 accept (q );
628640 if (q instanceof JsNumberLiteral ) {
629- /**
641+ /*
630642 * Fix for Issue #3796. "42.foo" is not allowed, but "42 .foo" is.
631643 */
632644 _space ();
@@ -690,13 +702,40 @@ public boolean visit(JsNumberLiteral x, JsContext ctx) {
690702
691703 long lvalue = (long ) dvalue ;
692704 if (lvalue == dvalue ) {
693- p .print (Long .toString (lvalue ));
705+ String longVal = Long .toString (lvalue );
706+ if (minifyLiterals && lvalue != 0 ) {
707+ int trailingZeros = numberOfTrailingDecZeros (longVal );
708+ if (trailingZeros > 2 ) {
709+ // print 1000 as 1e3, keep 100 as is
710+ longVal = longVal .substring (0 , longVal .length () - trailingZeros ) + "e" + trailingZeros ;
711+ } else if (Math .abs (lvalue ) > MAX_DECIMAL_VALUE ) {
712+ // from 1e12 we may save 1 or 2 bytes by using the hex code
713+ longVal = (lvalue < 0 ? "-0x" : "0x" ) + Long .toString (Math .abs (lvalue ), 16 );
714+ }
715+ }
716+ p .print (longVal );
694717 } else {
695- p .print (Double .toString (dvalue ));
718+ String doubleVal = Double .toString (dvalue );
719+ if (minifyLiterals ) {
720+ if (doubleVal .startsWith ("0." )) {
721+ doubleVal = doubleVal .substring (1 );
722+ } else if (doubleVal .startsWith ("-0." )) {
723+ doubleVal = "-" + doubleVal .substring (2 );
724+ }
725+ }
726+ p .print (doubleVal );
696727 }
697728 return false ;
698729 }
699730
731+ private int numberOfTrailingDecZeros (String longVal ) {
732+ int idx = longVal .length () - 1 ;
733+ while (longVal .charAt (idx ) == '0' ) {
734+ idx --;
735+ }
736+ return longVal .length () - idx - 1 ;
737+ }
738+
700739 @ Override
701740 public boolean visit (JsNumericEntry x , JsContext ctx ) {
702741 p .print (Integer .toString (x .getValue ()));
@@ -790,7 +829,11 @@ public boolean visit(JsReturn x, JsContext ctx) {
790829 _return ();
791830 JsExpression expr = x .getExpr ();
792831 if (expr != null ) {
793- _space ();
832+ if (spaceReturn (expr )) {
833+ _space ();
834+ } else {
835+ _spaceOpt ();
836+ }
794837 accept (expr );
795838 }
796839 return false ;
@@ -1275,6 +1318,20 @@ private boolean _spaceCalc(JsOperator op, JsExpression arg) {
12751318 return false ;
12761319 }
12771320
1321+ private boolean spaceReturn (JsExpression arg ) {
1322+ if (arg instanceof JsBooleanLiteral ) {
1323+ return !minifyLiterals ;
1324+ }
1325+ if (arg instanceof JsPrefixOperation ) {
1326+ return ((JsPrefixOperation ) arg ).getOperator ().isKeyword ();
1327+ }
1328+ if (arg instanceof JsNumberLiteral ) {
1329+ double value = ((JsNumberLiteral ) arg ).getValue ();
1330+ return Double .isNaN (value ) || value > 0 || 1 / value > 0 ;
1331+ }
1332+ return true ;
1333+ }
1334+
12781335 private void _spaceOpt () {
12791336 p .printOpt (' ' );
12801337 }
0 commit comments