-
Notifications
You must be signed in to change notification settings - Fork 384
Minify boolean and numeric literals in obfuscated mode #10291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
f50f053
edd9445
271e29c
7adbee5
cca743f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,9 +115,10 @@ public class JsToStringGenerationVisitor extends JsVisitor { | |
| * How many lines of code to print inside of a JsBlock when printing terse. | ||
| */ | ||
| private static final int JSBLOCK_LINES_TO_PRINT = 3; | ||
| private static final long MAX_DECIMAL_VALUE = 999_999_999_999L; | ||
|
|
||
| protected boolean needSemi = true; | ||
| private List<NamedRange> classRanges = new ArrayList<NamedRange>(); | ||
| private final List<NamedRange> classRanges = new ArrayList<>(); | ||
| private NamedRange currentClassRange; | ||
| private NamedRange programClassRange; | ||
|
|
||
|
|
@@ -127,27 +128,39 @@ public class JsToStringGenerationVisitor extends JsVisitor { | |
| * because the statements designated by statementEnds and statementStarts are | ||
| * those that appear directly within these global blocks. | ||
| */ | ||
| private Set<JsBlock> globalBlocks = new HashSet<JsBlock>(); | ||
| private final Set<JsBlock> globalBlocks = new HashSet<>(); | ||
| private final TextOutput p; | ||
| private ArrayList<Integer> statementEnds = new ArrayList<Integer>(); | ||
| private ArrayList<Integer> statementStarts = new ArrayList<Integer>(); | ||
| private final ArrayList<Integer> statementEnds = new ArrayList<>(); | ||
| private final ArrayList<Integer> statementStarts = new ArrayList<>(); | ||
| private final boolean useLongIdents; | ||
| private final boolean minifyLiterals; | ||
|
|
||
| public static class PrintOptions { | ||
| public final boolean useLongIdents; | ||
| public final boolean minifyLiterals; | ||
|
|
||
| public PrintOptions(boolean useLongIdents, boolean minifyLiterals) { | ||
| this.useLongIdents = useLongIdents; | ||
| this.minifyLiterals = minifyLiterals; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Generate the output string using short identifiers. | ||
| */ | ||
| public JsToStringGenerationVisitor(TextOutput out) { | ||
| this(out, false); | ||
| this(out, new PrintOptions(false, false)); | ||
| } | ||
|
|
||
| /** | ||
| * Generate the output string using short or long identifiers. | ||
| * | ||
| * @param useLongIdents if true, emit all identifiers in long form | ||
| * @param options settings for minification | ||
| */ | ||
| JsToStringGenerationVisitor(TextOutput out, boolean useLongIdents) { | ||
| JsToStringGenerationVisitor(TextOutput out, PrintOptions options) { | ||
| this.p = out; | ||
| this.useLongIdents = useLongIdents; | ||
| this.useLongIdents = options.useLongIdents; | ||
| this.minifyLiterals = options.minifyLiterals; | ||
| } | ||
|
|
||
| public List<NamedRange> getClassRanges() { | ||
|
|
@@ -183,8 +196,7 @@ public boolean visit(JsArrayAccess x, JsContext ctx) { | |
| public boolean visit(JsArrayLiteral x, JsContext ctx) { | ||
| _lsquare(); | ||
| boolean sep = false; | ||
| for (Object element : x.getExpressions()) { | ||
| JsExpression arg = (JsExpression) element; | ||
| for (JsExpression arg : x.getExpressions()) { | ||
| sep = _sepCommaOptSpace(sep); | ||
| _parenPushIfCommaExpr(arg); | ||
| accept(arg); | ||
|
|
@@ -227,6 +239,10 @@ public boolean visit(JsBlock x, JsContext ctx) { | |
|
|
||
| @Override | ||
| public boolean visit(JsBooleanLiteral x, JsContext ctx) { | ||
| if (minifyLiterals) { | ||
| p.print(x.getValue() ? "!0" : "!1"); | ||
| return false; | ||
| } | ||
| if (x.getValue()) { | ||
| _true(); | ||
| } else { | ||
|
|
@@ -257,8 +273,7 @@ public boolean visit(JsCase x, JsContext ctx) { | |
| _newlineOpt(); | ||
|
|
||
| indent(); | ||
| for (Object element : x.getStmts()) { | ||
| JsStatement stmt = (JsStatement) element; | ||
| for (JsStatement stmt : x.getStmts()) { | ||
| needSemi = true; | ||
| accept(stmt); | ||
| if (needSemi) { | ||
|
|
@@ -387,8 +402,7 @@ public boolean visit(JsDefault x, JsContext ctx) { | |
| _colon(); | ||
|
|
||
| indent(); | ||
| for (Object element : x.getStmts()) { | ||
| JsStatement stmt = (JsStatement) element; | ||
| for (JsStatement stmt : x.getStmts()) { | ||
| needSemi = true; | ||
| accept(stmt); | ||
| if (needSemi) { | ||
|
|
@@ -532,8 +546,7 @@ public boolean visit(JsFunction x, JsContext ctx) { | |
|
|
||
| _lparen(); | ||
| boolean sep = false; | ||
| for (Object element : x.getParameters()) { | ||
| JsParameter param = (JsParameter) element; | ||
| for (JsParameter param : x.getParameters()) { | ||
| sep = _sepCommaOptSpace(sep); | ||
| accept(param); | ||
| } | ||
|
|
@@ -588,8 +601,7 @@ public boolean visit(JsInvocation x, JsContext ctx) { | |
|
|
||
| _lparen(); | ||
| boolean sep = false; | ||
| for (Object element : x.getArguments()) { | ||
| JsExpression arg = (JsExpression) element; | ||
| for (JsExpression arg : x.getArguments()) { | ||
| sep = _sepCommaOptSpace(sep); | ||
| _parenPushIfCommaExpr(arg); | ||
| accept(arg); | ||
|
|
@@ -626,7 +638,7 @@ public boolean visit(JsNameRef x, JsContext ctx) { | |
| _parenPush(x, q, false); | ||
| accept(q); | ||
| if (q instanceof JsNumberLiteral) { | ||
| /** | ||
| /* | ||
| * Fix for Issue #3796. "42.foo" is not allowed, but "42 .foo" is. | ||
| */ | ||
| _space(); | ||
|
|
@@ -681,20 +693,51 @@ public boolean visit(JsNullLiteral x, JsContext ctx) { | |
|
|
||
| @Override | ||
| public boolean visit(JsNumberLiteral x, JsContext ctx) { | ||
| String val = stringifyNumber(x); | ||
| p.print(val); | ||
| return false; | ||
| } | ||
|
|
||
| private String stringifyNumber(JsNumberLiteral x) { | ||
| double dvalue = x.getValue(); | ||
| if (dvalue == 0.0 && 1.0 / dvalue == Double.NEGATIVE_INFINITY) { | ||
| // Negative zero is distinct from 0.0 and (integer) 0 | ||
| p.print("-0."); | ||
| return false; | ||
| return "-0"; | ||
| } | ||
|
|
||
| long lvalue = (long) dvalue; | ||
| if (lvalue == dvalue) { | ||
| p.print(Long.toString(lvalue)); | ||
| String longVal = Long.toString(lvalue); | ||
| if (minifyLiterals && lvalue != 0) { | ||
| int trailingZeros = numberOfTrailingDecZeros(longVal); | ||
| if (trailingZeros > 2) { | ||
| // print 1000 as 1e3, keep 100 as is | ||
| longVal = longVal.substring(0, longVal.length() - trailingZeros) + "e" + trailingZeros; | ||
| } else if (Math.abs(lvalue) > MAX_DECIMAL_VALUE) { | ||
| // from 1e12 we may save 1 or 2 bytes by using the hex code | ||
| longVal = (lvalue < 0 ? "-0x" : "0x") + Long.toString(Math.abs(lvalue), 16); | ||
| } | ||
| } | ||
| return longVal; | ||
| } else { | ||
| p.print(Double.toString(dvalue)); | ||
| String doubleVal = Double.toString(dvalue); | ||
| if (minifyLiterals) { | ||
| if (doubleVal.startsWith("0.")) { | ||
| doubleVal = doubleVal.substring(1); | ||
| } else if (doubleVal.startsWith("-0.")) { | ||
| doubleVal = "-" + doubleVal.substring(2); | ||
| } | ||
| } | ||
| return doubleVal; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private int numberOfTrailingDecZeros(String longVal) { | ||
| int idx = longVal.length() - 1; | ||
| while (longVal.charAt(idx) == '0') { | ||
| idx--; | ||
| } | ||
| return longVal.length() - idx - 1; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -790,7 +833,11 @@ public boolean visit(JsReturn x, JsContext ctx) { | |
| _return(); | ||
| JsExpression expr = x.getExpr(); | ||
| if (expr != null) { | ||
| _space(); | ||
| if (spaceReturn(expr)) { | ||
| _space(); | ||
| } else { | ||
| _spaceOpt(); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a great solution for this, but I just wanted to call out briefly that this seems gross - we have to toString the child node twice, so that we know how to concat them together. Perf-wise (though probably not logic-wise) it would seem better to just go ahead and not accept(expr) if we know it is a literal, but append directly. Better still would be to signal to the TextOutput "hey you already know if space is optional or not... I just finished writing a keyword, make sure the next token gets a space if it needs it". Then, when the next token is written, TextOutput decides if it needs a space? One missing case in the current impl is to do with parens - in the terser sample you showed me, there are some cases of extra parens being required in order to maintain operator precedence:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's implemented in my last commit.
I don't have a good solution for that -- would it be OK to leave that for another time?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think we wait, rethink that as we implement other size enhancements.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uh, so we need to |
||
| accept(expr); | ||
| } | ||
| return false; | ||
|
|
@@ -1275,6 +1322,20 @@ private boolean _spaceCalc(JsOperator op, JsExpression arg) { | |
| return false; | ||
| } | ||
|
|
||
| private boolean spaceReturn(JsExpression arg) { | ||
|
zbynek marked this conversation as resolved.
Outdated
|
||
| if (arg instanceof JsBooleanLiteral) { | ||
| return !minifyLiterals; | ||
| } | ||
| if (arg instanceof JsPrefixOperation) { | ||
| return ((JsPrefixOperation) arg).getOperator().isKeyword(); | ||
| } | ||
| if (arg instanceof JsNumberLiteral) { | ||
| String value = stringifyNumber((JsNumberLiteral) arg); | ||
| return value.charAt(0) != '-' && value.charAt(0) != '.'; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| private void _spaceOpt() { | ||
| p.printOpt(' '); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.