aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-03-03 18:04:32 +0800
committerGitHub <noreply@github.com>2017-03-03 18:04:32 +0800
commit18059cc94fdc037e296a1cb1b08143d5e3aae570 (patch)
treed8b787fba1df5fe5c5052354008084c9f74e58d3 /lib
parentb5e0e8c2038c7c0ea13771891eb84f6e6f7bcbc3 (diff)
downloadtracifyjs-18059cc94fdc037e296a1cb1b08143d5e3aae570.tar.gz
tracifyjs-18059cc94fdc037e296a1cb1b08143d5e3aae570.zip
compress numerical expressions (#1513)
safe operations - `a === b` => `a == b` - `a + -b` => `a - b` - `-a + b` => `b - a` - `a+ +b` => `+b+a` associative operations (bit-wise operations are safe, otherwise `unsafe_math`) - `a + (b + c)` => `(a + b) + c` - `(n + 2) + 3` => `5 + n` - `(2 * n) * 3` => `6 * n` - `(a | 1) | (2 | d)` => `(3 | a) | b` fixes #412
Diffstat (limited to 'lib')
-rw-r--r--lib/compress.js171
1 files changed, 164 insertions, 7 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 38ebbf40..ec1e7174 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -54,6 +54,7 @@ function Compressor(options, false_by_default) {
drop_debugger : !false_by_default,
unsafe : false,
unsafe_comps : false,
+ unsafe_math : false,
unsafe_proto : false,
conditionals : !false_by_default,
comparisons : !false_by_default,
@@ -1043,6 +1044,34 @@ merge(Compressor.prototype, {
node.DEFMETHOD("is_boolean", func);
});
+ // methods to determine if an expression has a numeric result type
+ (function (def){
+ def(AST_Node, return_false);
+ def(AST_Number, return_true);
+ var unary = makePredicate("+ - ~ ++ --");
+ def(AST_Unary, function(){
+ return unary(this.operator);
+ });
+ var binary = makePredicate("- * / % & | ^ << >> >>>");
+ def(AST_Binary, function(compressor){
+ return binary(this.operator) || this.operator == "+"
+ && this.left.is_number(compressor)
+ && this.right.is_number(compressor);
+ });
+ var assign = makePredicate("-= *= /= %= &= |= ^= <<= >>= >>>=");
+ def(AST_Assign, function(compressor){
+ return assign(this.operator) || this.right.is_number(compressor);
+ });
+ def(AST_Seq, function(compressor){
+ return this.cdr.is_number(compressor);
+ });
+ def(AST_Conditional, function(compressor){
+ return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
+ });
+ })(function(node, func){
+ node.DEFMETHOD("is_number", func);
+ });
+
// methods to determine if an expression has a string result type
(function (def){
def(AST_Node, return_false);
@@ -2867,8 +2896,14 @@ merge(Compressor.prototype, {
right: rhs[0]
}).optimize(compressor);
}
- function reverse(op, force) {
- if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
+ function reversible() {
+ return self.left instanceof AST_Constant
+ || self.right instanceof AST_Constant
+ || !self.left.has_side_effects(compressor)
+ && !self.right.has_side_effects(compressor);
+ }
+ function reverse(op) {
+ if (reversible()) {
if (op) self.operator = op;
var tmp = self.left;
self.left = self.right;
@@ -2884,7 +2919,7 @@ merge(Compressor.prototype, {
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
- reverse(null, true);
+ reverse();
}
}
if (/^[!=]==?$/.test(self.operator)) {
@@ -2919,6 +2954,7 @@ merge(Compressor.prototype, {
case "===":
case "!==":
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
+ (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean())) {
self.operator = self.operator.substr(0, 2);
}
@@ -3056,7 +3092,10 @@ merge(Compressor.prototype, {
}
break;
}
- if (self.operator == "+") {
+ var associative = true;
+ switch (self.operator) {
+ case "+":
+ // "foo" + ("bar" + x) => "foobar" + x
if (self.left instanceof AST_Constant
&& self.right instanceof AST_Binary
&& self.right.operator == "+"
@@ -3064,7 +3103,7 @@ merge(Compressor.prototype, {
&& self.right.is_string(compressor)) {
self = make_node(AST_Binary, self, {
operator: "+",
- left: make_node(AST_String, null, {
+ left: make_node(AST_String, self.left, {
value: "" + self.left.getValue() + self.right.left.getValue(),
start: self.left.start,
end: self.right.left.end
@@ -3072,6 +3111,7 @@ merge(Compressor.prototype, {
right: self.right.right
});
}
+ // (x + "foo") + "bar" => x + "foobar"
if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary
&& self.left.operator == "+"
@@ -3080,13 +3120,14 @@ merge(Compressor.prototype, {
self = make_node(AST_Binary, self, {
operator: "+",
left: self.left.left,
- right: make_node(AST_String, null, {
+ right: make_node(AST_String, self.right, {
value: "" + self.left.right.getValue() + self.right.getValue(),
start: self.left.right.start,
end: self.right.end
})
});
}
+ // (x + "foo") + ("bar" + y) => (x + "foobar") + y
if (self.left instanceof AST_Binary
&& self.left.operator == "+"
&& self.left.is_string(compressor)
@@ -3100,7 +3141,7 @@ merge(Compressor.prototype, {
left: make_node(AST_Binary, self.left, {
operator: "+",
left: self.left.left,
- right: make_node(AST_String, null, {
+ right: make_node(AST_String, self.left.right, {
value: "" + self.left.right.getValue() + self.right.left.getValue(),
start: self.left.right.start,
end: self.right.left.end
@@ -3109,6 +3150,122 @@ merge(Compressor.prototype, {
right: self.right.right
});
}
+ // a + -b => a - b
+ if (self.right instanceof AST_UnaryPrefix
+ && self.right.operator == "-"
+ && self.left.is_number(compressor)) {
+ self = make_node(AST_Binary, self, {
+ operator: "-",
+ left: self.left,
+ right: self.right.expression
+ });
+ }
+ // -a + b => b - a
+ if (self.left instanceof AST_UnaryPrefix
+ && self.left.operator == "-"
+ && reversible()
+ && self.right.is_number(compressor)) {
+ self = make_node(AST_Binary, self, {
+ operator: "-",
+ left: self.right,
+ right: self.left.expression
+ });
+ }
+ case "*":
+ associative = compressor.option("unsafe_math");
+ case "&":
+ case "|":
+ case "^":
+ // a + +b => +b + a
+ if (self.left.is_number(compressor)
+ && self.right.is_number(compressor)
+ && reversible()
+ && !(self.left instanceof AST_Binary
+ && self.left.operator != self.operator
+ && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
+ var reversed = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: self.right,
+ right: self.left
+ });
+ if (self.right instanceof AST_Constant
+ && !(self.left instanceof AST_Constant)) {
+ self = best_of(reversed, self);
+ } else {
+ self = best_of(self, reversed);
+ }
+ }
+ if (associative && self.is_number(compressor)) {
+ // a + (b + c) => (a + b) + c
+ if (self.right instanceof AST_Binary
+ && self.right.operator == self.operator) {
+ self = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: make_node(AST_Binary, self.left, {
+ operator: self.operator,
+ left: self.left,
+ right: self.right.left,
+ start: self.left.start,
+ end: self.right.left.end
+ }),
+ right: self.right.right
+ });
+ }
+ // (n + 2) + 3 => 5 + n
+ // (2 * n) * 3 => 6 + n
+ if (self.right instanceof AST_Constant
+ && self.left instanceof AST_Binary
+ && self.left.operator == self.operator) {
+ if (self.left.left instanceof AST_Constant) {
+ self = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: make_node(AST_Binary, self.left, {
+ operator: self.operator,
+ left: self.left.left,
+ right: self.right,
+ start: self.left.left.start,
+ end: self.right.end
+ }),
+ right: self.left.right
+ });
+ } else if (self.left.right instanceof AST_Constant) {
+ self = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: make_node(AST_Binary, self.left, {
+ operator: self.operator,
+ left: self.left.right,
+ right: self.right,
+ start: self.left.right.start,
+ end: self.right.end
+ }),
+ right: self.left.left
+ });
+ }
+ }
+ // (a | 1) | (2 | d) => (3 | a) | b
+ if (self.left instanceof AST_Binary
+ && self.left.operator == self.operator
+ && self.left.right instanceof AST_Constant
+ && self.right instanceof AST_Binary
+ && self.right.operator == self.operator
+ && self.right.left instanceof AST_Constant) {
+ self = make_node(AST_Binary, self, {
+ operator: self.operator,
+ left: make_node(AST_Binary, self.left, {
+ operator: self.operator,
+ left: make_node(AST_Binary, self.left.left, {
+ operator: self.operator,
+ left: self.left.right,
+ right: self.right.left,
+ start: self.left.right.start,
+ end: self.right.left.end
+ }),
+ right: self.left.left
+ }),
+ right: self.right.right
+ });
+ }
+ }
}
}
// x && (y && z) ==> x && y && z