improve compress efficiency (#5220)
This commit is contained in:
176
lib/compress.js
176
lib/compress.js
@@ -7929,12 +7929,17 @@ Compressor.prototype.compress = function(node) {
|
||||
function drop_returns(compressor, exp) {
|
||||
var arrow = is_arrow(exp);
|
||||
var async = is_async(exp);
|
||||
var changed = false;
|
||||
var drop_body = false;
|
||||
if (arrow && compressor.option("arrows")) {
|
||||
if (!exp.value) {
|
||||
drop_body = true;
|
||||
} else if (!async || is_primitive(compressor, exp.value)) {
|
||||
exp.value = exp.value.drop_side_effect_free(compressor);
|
||||
var dropped = exp.value.drop_side_effect_free(compressor);
|
||||
if (dropped !== exp.value) {
|
||||
changed = true;
|
||||
exp.value = dropped;
|
||||
}
|
||||
}
|
||||
} else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
|
||||
if (exp.name) {
|
||||
@@ -7951,6 +7956,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (async && !is_primitive(compressor, value)) return node;
|
||||
value = value.drop_side_effect_free(compressor, true);
|
||||
}
|
||||
changed = true;
|
||||
if (!value) return make_node(AST_EmptyStatement, node);
|
||||
return make_node(AST_SimpleStatement, node, { body: value });
|
||||
});
|
||||
@@ -7958,7 +7964,11 @@ Compressor.prototype.compress = function(node) {
|
||||
var value = node.value;
|
||||
if (value) {
|
||||
if (async && !is_primitive(compressor, value)) return;
|
||||
node.value = value.drop_side_effect_free(compressor);
|
||||
var dropped = value.drop_side_effect_free(compressor);
|
||||
if (dropped !== value) {
|
||||
changed = true;
|
||||
node.value = dropped;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -7967,6 +7977,7 @@ Compressor.prototype.compress = function(node) {
|
||||
var body = node.body;
|
||||
if (body instanceof AST_Await) {
|
||||
if (is_primitive(compressor, body.expression)) {
|
||||
changed = true;
|
||||
body = body.expression.drop_side_effect_free(compressor, true);
|
||||
if (!body) return make_node(AST_EmptyStatement, node);
|
||||
node.body = body;
|
||||
@@ -7976,8 +7987,10 @@ Compressor.prototype.compress = function(node) {
|
||||
for (var i = exprs.length; --i >= 0;) {
|
||||
var tail = exprs[i];
|
||||
if (!(tail instanceof AST_Await)) break;
|
||||
if (!is_primitive(compressor, tail.expression)) break;
|
||||
if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
|
||||
var value = tail.expression;
|
||||
if (!is_primitive(compressor, value)) break;
|
||||
changed = true;
|
||||
if (exprs[i] = value.drop_side_effect_free(compressor)) break;
|
||||
}
|
||||
switch (i) {
|
||||
case -1:
|
||||
@@ -8021,7 +8034,7 @@ Compressor.prototype.compress = function(node) {
|
||||
return make_node(ctor, exp, exp);
|
||||
}
|
||||
}
|
||||
return drop_body && exp.clone();
|
||||
return changed && exp.clone();
|
||||
}
|
||||
def(AST_Call, function(compressor, first_in_statement) {
|
||||
var self = this;
|
||||
@@ -10369,42 +10382,46 @@ Compressor.prototype.compress = function(node) {
|
||||
OPT(AST_UnaryPrefix, function(self, compressor) {
|
||||
var op = self.operator;
|
||||
var exp = self.expression;
|
||||
if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
|
||||
return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
|
||||
}
|
||||
if (compressor.option("sequences") && can_lift()) {
|
||||
var seq = lift_sequence_in_expression(self, compressor);
|
||||
if (seq !== self) return seq.optimize(compressor);
|
||||
}
|
||||
if (compressor.option("side_effects") && op == "void") {
|
||||
switch (op) {
|
||||
case "-":
|
||||
if (exp instanceof AST_Infinity) exp = exp.transform(compressor);
|
||||
// avoids infinite recursion of numerals
|
||||
if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self;
|
||||
break;
|
||||
case "!":
|
||||
if (!compressor.option("booleans")) break;
|
||||
if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
|
||||
if (compressor.in_boolean_context()) {
|
||||
// !!foo ---> foo, if we're in boolean context
|
||||
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression;
|
||||
if (exp instanceof AST_Binary) {
|
||||
self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "delete":
|
||||
if (!compressor.option("evaluate")) break;
|
||||
if (may_not_delete(exp)) break;
|
||||
return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
|
||||
case "typeof":
|
||||
if (!compressor.option("booleans")) break;
|
||||
if (!compressor.in_boolean_context()) break;
|
||||
// typeof always returns a non-empty string, thus always truthy
|
||||
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||
var exprs = [ make_node(AST_True, self) ];
|
||||
if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
|
||||
return make_sequence(self, exprs).optimize(compressor);
|
||||
case "void":
|
||||
if (!compressor.option("side_effects")) break;
|
||||
exp = exp.drop_side_effect_free(compressor);
|
||||
if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
|
||||
self.expression = exp;
|
||||
return self;
|
||||
}
|
||||
if (compressor.option("booleans")) {
|
||||
if (op == "!" && exp.is_truthy()) {
|
||||
return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
|
||||
} else if (compressor.in_boolean_context()) switch (op) {
|
||||
case "!":
|
||||
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
|
||||
// !!foo ---> foo, if we're in boolean context
|
||||
return exp.expression;
|
||||
}
|
||||
if (exp instanceof AST_Binary) {
|
||||
self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
|
||||
}
|
||||
break;
|
||||
case "typeof":
|
||||
// typeof always returns a non-empty string, thus it's
|
||||
// always true in booleans
|
||||
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||
var exprs = [ make_node(AST_True, self) ];
|
||||
if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
|
||||
return make_sequence(self, exprs).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
|
||||
if (compressor.option("evaluate")
|
||||
&& exp instanceof AST_Binary
|
||||
&& SIGN_OPS[op]
|
||||
@@ -10414,14 +10431,12 @@ Compressor.prototype.compress = function(node) {
|
||||
operator: exp.operator,
|
||||
left: make_node(AST_UnaryPrefix, exp.left, {
|
||||
operator: op,
|
||||
expression: exp.left
|
||||
expression: exp.left,
|
||||
}),
|
||||
right: exp.right
|
||||
right: exp.right,
|
||||
});
|
||||
}
|
||||
// avoids infinite recursion of numerals
|
||||
return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
|
||||
? self : try_evaluate(compressor, self);
|
||||
return try_evaluate(compressor, self);
|
||||
|
||||
function may_not_delete(node) {
|
||||
return node instanceof AST_Infinity
|
||||
@@ -10582,32 +10597,6 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
|
||||
OPT(AST_Binary, function(self, compressor) {
|
||||
function reversible() {
|
||||
return self.left.is_constant()
|
||||
|| self.right.is_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;
|
||||
self.right = tmp;
|
||||
}
|
||||
}
|
||||
function swap_chain() {
|
||||
var rhs = self.right;
|
||||
self.left = make_node(AST_Binary, self, {
|
||||
operator: self.operator,
|
||||
left: self.left,
|
||||
right: rhs.left,
|
||||
start: self.left.start,
|
||||
end: rhs.left.end
|
||||
});
|
||||
self.right = rhs.right;
|
||||
self.left = self.left.transform(compressor);
|
||||
}
|
||||
if (commutativeOperators[self.operator]
|
||||
&& self.right.is_constant()
|
||||
&& !self.left.is_constant()
|
||||
@@ -11260,6 +11249,35 @@ Compressor.prototype.compress = function(node) {
|
||||
&& self.left.expression instanceof AST_Number && self.left.expression.value == 1;
|
||||
}
|
||||
}
|
||||
|
||||
function reversible() {
|
||||
return self.left.is_constant()
|
||||
|| self.right.is_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;
|
||||
self.right = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function swap_chain() {
|
||||
var rhs = self.right;
|
||||
self.left = make_node(AST_Binary, self, {
|
||||
operator: self.operator,
|
||||
left: self.left,
|
||||
right: rhs.left,
|
||||
start: self.left.start,
|
||||
end: rhs.left.end
|
||||
});
|
||||
self.right = rhs.right;
|
||||
self.left = self.left.transform(compressor);
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_SymbolExport, function(self) {
|
||||
@@ -11593,9 +11611,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (lhs && is_atomic(lhs, self)) return self;
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator: "void",
|
||||
expression: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
expression: make_node(AST_Number, self, { value: 0 }),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11607,12 +11623,8 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
left: make_node(AST_Number, self, {
|
||||
value: 1
|
||||
}),
|
||||
right: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
left: make_node(AST_Number, self, { value: 1 }),
|
||||
right: make_node(AST_Number, self, { value: 0 }),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11622,12 +11634,8 @@ Compressor.prototype.compress = function(node) {
|
||||
if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
left: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
}),
|
||||
right: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
left: make_node(AST_Number, self, { value: 0 }),
|
||||
right: make_node(AST_Number, self, { value: 0 }),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12199,9 +12207,7 @@ Compressor.prototype.compress = function(node) {
|
||||
|
||||
OPT(AST_Boolean, function(self, compressor) {
|
||||
if (!compressor.option("booleans")) return self;
|
||||
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
|
||||
value: +self.value
|
||||
});
|
||||
if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value });
|
||||
var p = compressor.parent();
|
||||
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
|
||||
AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|
||||
@@ -12211,15 +12217,11 @@ Compressor.prototype.compress = function(node) {
|
||||
line : p.start.line,
|
||||
col : p.start.col,
|
||||
});
|
||||
return make_node(AST_Number, self, {
|
||||
value: +self.value
|
||||
});
|
||||
return make_node(AST_Number, self, { value: +self.value });
|
||||
}
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: make_node(AST_Number, self, {
|
||||
value: 1 - self.value
|
||||
})
|
||||
expression: make_node(AST_Number, self, { value: 1 - self.value }),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -508,7 +508,7 @@ drop_value: {
|
||||
((a, b) => a + b)(console.log(42));
|
||||
}
|
||||
expect: {
|
||||
((a, b) => {})(console.log(42));
|
||||
void console.log(42);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
|
||||
@@ -890,7 +890,7 @@ keep_fnames: {
|
||||
issue_805_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
passes: 3,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
@@ -926,7 +926,7 @@ issue_805_1: {
|
||||
issue_805_2: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
passes: 3,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
|
||||
@@ -1765,7 +1765,7 @@ issue_2846: {
|
||||
issue_805_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
passes: 3,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
@@ -1798,7 +1798,7 @@ issue_805_1: {
|
||||
issue_805_2: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
passes: 3,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
|
||||
@@ -3862,6 +3862,7 @@ issue_3679_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
|
||||
@@ -389,6 +389,7 @@ issue_1288_side_effects: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
negate_iife: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
@@ -409,10 +410,10 @@ issue_1288_side_effects: {
|
||||
})(0);
|
||||
}
|
||||
expect: {
|
||||
w;
|
||||
w,
|
||||
x || function() {
|
||||
x = {};
|
||||
}();
|
||||
}(),
|
||||
y;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,9 +866,9 @@ issue_4575: {
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
(function a(...d) {
|
||||
(function(d) {
|
||||
console.log(d.length);
|
||||
})();
|
||||
})([]);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "0"
|
||||
|
||||
Reference in New Issue
Block a user