drop_debugger : !false_by_default,
unsafe : false,
unsafe_comps : false,
+ unsafe_math : false,
unsafe_proto : false,
conditionals : !false_by_default,
comparisons : !false_by_default,
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);
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;
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
- reverse(null, true);
+ reverse();
}
}
if (/^[!=]==?$/.test(self.operator)) {
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);
}
}
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 == "+"
&& 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
right: self.right.right
});
}
+ // (x + "foo") + "bar" => x + "foobar"
if (self.right instanceof AST_Constant
&& self.left instanceof AST_Binary
&& self.left.operator == "+"
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)
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
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