improve compression of undefined, NaN & Infinitiy (#1748)
- migrate transformation logic from `OutputStream` to `Compressor` - always turn `undefined` into `void 0` (unless `unsafe`) - always keep `NaN` except when avoiding local variable redefinition - introduce `keep_infinity` to suppress `1/0` transform, except when avoiding local variable redefinition supersedes #1723 fixes #1730
This commit is contained in:
@@ -66,6 +66,7 @@ function Compressor(options, false_by_default) {
|
||||
join_vars : !false_by_default,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
keep_infinity : false,
|
||||
loops : !false_by_default,
|
||||
negate_iife : !false_by_default,
|
||||
passes : 1,
|
||||
@@ -215,7 +216,12 @@ merge(Compressor.prototype, {
|
||||
}) : make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
return make_node(AST_SimpleStatement, node, {
|
||||
body: node.value || make_node(AST_Undefined, node)
|
||||
body: node.value || make_node(AST_UnaryPrefix, node, {
|
||||
operator: "void",
|
||||
expression: make_node(AST_Number, node, {
|
||||
value: 0
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
if (node instanceof AST_Lambda && node !== self) {
|
||||
@@ -1123,8 +1129,12 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
};
|
||||
|
||||
function is_undefined(node) {
|
||||
return node instanceof AST_Undefined || node.is_undefined;
|
||||
function is_undefined(node, compressor) {
|
||||
return node.is_undefined
|
||||
|| node instanceof AST_Undefined
|
||||
|| node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "void"
|
||||
&& !node.expression.has_side_effects(compressor);
|
||||
}
|
||||
|
||||
/* -----[ boolean/negation helpers ]----- */
|
||||
@@ -1313,7 +1323,7 @@ merge(Compressor.prototype, {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
var unaryPrefix = makePredicate("! ~ - +");
|
||||
var unaryPrefix = makePredicate("! ~ - + void");
|
||||
AST_Node.DEFMETHOD("is_constant", function(){
|
||||
// Accomodate when compress option evaluate=false
|
||||
// as well as the common constant expressions !0 and -1
|
||||
@@ -2971,7 +2981,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_undefined(self.cdr)) {
|
||||
if (is_undefined(self.cdr, compressor)) {
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator : "void",
|
||||
expression : self.car
|
||||
@@ -3010,7 +3020,7 @@ merge(Compressor.prototype, {
|
||||
self.expression = e;
|
||||
return self;
|
||||
} else {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
@@ -3034,6 +3044,9 @@ merge(Compressor.prototype, {
|
||||
})).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (self.operator == "-" && e instanceof AST_Infinity) {
|
||||
e = e.transform(compressor);
|
||||
}
|
||||
if (e instanceof AST_Binary
|
||||
&& (self.operator == "+" || self.operator == "-")
|
||||
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
|
||||
@@ -3043,8 +3056,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
// avoids infinite recursion of numerals
|
||||
if (self.operator != "-"
|
||||
|| !(self.expression instanceof AST_Number
|
||||
|| self.expression instanceof AST_Infinity)) {
|
||||
|| !(e instanceof AST_Number || e instanceof AST_Infinity)) {
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
@@ -3087,8 +3099,8 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Binary, function(self, compressor){
|
||||
function reversible() {
|
||||
return self.left instanceof AST_Constant
|
||||
|| self.right instanceof AST_Constant
|
||||
return self.left.is_constant()
|
||||
|| self.right.is_constant()
|
||||
|| !self.left.has_side_effects(compressor)
|
||||
&& !self.right.has_side_effects(compressor);
|
||||
}
|
||||
@@ -3101,8 +3113,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (commutativeOperators(self.operator)) {
|
||||
if (self.right instanceof AST_Constant
|
||||
&& !(self.left instanceof AST_Constant)) {
|
||||
if (self.right.is_constant()
|
||||
&& !self.left.is_constant()) {
|
||||
// if right is a constant, whatever side effects the
|
||||
// left side might have could not influence the
|
||||
// result. hence, force switch.
|
||||
@@ -3464,9 +3476,9 @@ merge(Compressor.prototype, {
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
case "NaN":
|
||||
return make_node(AST_NaN, self);
|
||||
return make_node(AST_NaN, self).optimize(compressor);
|
||||
case "Infinity":
|
||||
return make_node(AST_Infinity, self);
|
||||
return make_node(AST_Infinity, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
||||
@@ -3508,7 +3520,38 @@ merge(Compressor.prototype, {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator: "void",
|
||||
expression: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
OPT(AST_Infinity, function(self, compressor){
|
||||
var retain = compressor.option("keep_infinity")
|
||||
&& !compressor.find_parent(AST_Scope).find_variable("Infinity");
|
||||
return retain ? self : make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
left: make_node(AST_Number, self, {
|
||||
value: 1
|
||||
}),
|
||||
right: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
OPT(AST_NaN, function(self, compressor){
|
||||
return compressor.find_parent(AST_Scope).find_variable("NaN") ? make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
left: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
}),
|
||||
right: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
}) : self;
|
||||
});
|
||||
|
||||
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
||||
@@ -3809,7 +3852,7 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_RegExp, literals_in_boolean_context);
|
||||
|
||||
OPT(AST_Return, function(self, compressor){
|
||||
if (self.value && is_undefined(self.value)) {
|
||||
if (self.value && is_undefined(self.value, compressor)) {
|
||||
self.value = null;
|
||||
}
|
||||
return self;
|
||||
|
||||
Reference in New Issue
Block a user