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) {
|
function drop_returns(compressor, exp) {
|
||||||
var arrow = is_arrow(exp);
|
var arrow = is_arrow(exp);
|
||||||
var async = is_async(exp);
|
var async = is_async(exp);
|
||||||
|
var changed = false;
|
||||||
var drop_body = false;
|
var drop_body = false;
|
||||||
if (arrow && compressor.option("arrows")) {
|
if (arrow && compressor.option("arrows")) {
|
||||||
if (!exp.value) {
|
if (!exp.value) {
|
||||||
drop_body = true;
|
drop_body = true;
|
||||||
} else if (!async || is_primitive(compressor, exp.value)) {
|
} 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) {
|
} else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
|
||||||
if (exp.name) {
|
if (exp.name) {
|
||||||
@@ -7951,6 +7956,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
if (async && !is_primitive(compressor, value)) return node;
|
if (async && !is_primitive(compressor, value)) return node;
|
||||||
value = value.drop_side_effect_free(compressor, true);
|
value = value.drop_side_effect_free(compressor, true);
|
||||||
}
|
}
|
||||||
|
changed = true;
|
||||||
if (!value) return make_node(AST_EmptyStatement, node);
|
if (!value) return make_node(AST_EmptyStatement, node);
|
||||||
return make_node(AST_SimpleStatement, node, { body: value });
|
return make_node(AST_SimpleStatement, node, { body: value });
|
||||||
});
|
});
|
||||||
@@ -7958,7 +7964,11 @@ Compressor.prototype.compress = function(node) {
|
|||||||
var value = node.value;
|
var value = node.value;
|
||||||
if (value) {
|
if (value) {
|
||||||
if (async && !is_primitive(compressor, value)) return;
|
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;
|
var body = node.body;
|
||||||
if (body instanceof AST_Await) {
|
if (body instanceof AST_Await) {
|
||||||
if (is_primitive(compressor, body.expression)) {
|
if (is_primitive(compressor, body.expression)) {
|
||||||
|
changed = true;
|
||||||
body = body.expression.drop_side_effect_free(compressor, true);
|
body = body.expression.drop_side_effect_free(compressor, true);
|
||||||
if (!body) return make_node(AST_EmptyStatement, node);
|
if (!body) return make_node(AST_EmptyStatement, node);
|
||||||
node.body = body;
|
node.body = body;
|
||||||
@@ -7976,8 +7987,10 @@ Compressor.prototype.compress = function(node) {
|
|||||||
for (var i = exprs.length; --i >= 0;) {
|
for (var i = exprs.length; --i >= 0;) {
|
||||||
var tail = exprs[i];
|
var tail = exprs[i];
|
||||||
if (!(tail instanceof AST_Await)) break;
|
if (!(tail instanceof AST_Await)) break;
|
||||||
if (!is_primitive(compressor, tail.expression)) break;
|
var value = tail.expression;
|
||||||
if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
|
if (!is_primitive(compressor, value)) break;
|
||||||
|
changed = true;
|
||||||
|
if (exprs[i] = value.drop_side_effect_free(compressor)) break;
|
||||||
}
|
}
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case -1:
|
case -1:
|
||||||
@@ -8021,7 +8034,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
return make_node(ctor, exp, exp);
|
return make_node(ctor, exp, exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return drop_body && exp.clone();
|
return changed && exp.clone();
|
||||||
}
|
}
|
||||||
def(AST_Call, function(compressor, first_in_statement) {
|
def(AST_Call, function(compressor, first_in_statement) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -10369,42 +10382,46 @@ Compressor.prototype.compress = function(node) {
|
|||||||
OPT(AST_UnaryPrefix, function(self, compressor) {
|
OPT(AST_UnaryPrefix, function(self, compressor) {
|
||||||
var op = self.operator;
|
var op = self.operator;
|
||||||
var exp = self.expression;
|
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()) {
|
if (compressor.option("sequences") && can_lift()) {
|
||||||
var seq = lift_sequence_in_expression(self, compressor);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(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);
|
exp = exp.drop_side_effect_free(compressor);
|
||||||
if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
|
if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
|
||||||
self.expression = exp;
|
self.expression = exp;
|
||||||
return self;
|
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")
|
if (compressor.option("evaluate")
|
||||||
&& exp instanceof AST_Binary
|
&& exp instanceof AST_Binary
|
||||||
&& SIGN_OPS[op]
|
&& SIGN_OPS[op]
|
||||||
@@ -10414,14 +10431,12 @@ Compressor.prototype.compress = function(node) {
|
|||||||
operator: exp.operator,
|
operator: exp.operator,
|
||||||
left: make_node(AST_UnaryPrefix, exp.left, {
|
left: make_node(AST_UnaryPrefix, exp.left, {
|
||||||
operator: op,
|
operator: op,
|
||||||
expression: exp.left
|
expression: exp.left,
|
||||||
}),
|
}),
|
||||||
right: exp.right
|
right: exp.right,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// avoids infinite recursion of numerals
|
return try_evaluate(compressor, self);
|
||||||
return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
|
|
||||||
? self : try_evaluate(compressor, self);
|
|
||||||
|
|
||||||
function may_not_delete(node) {
|
function may_not_delete(node) {
|
||||||
return node instanceof AST_Infinity
|
return node instanceof AST_Infinity
|
||||||
@@ -10582,32 +10597,6 @@ Compressor.prototype.compress = function(node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor) {
|
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]
|
if (commutativeOperators[self.operator]
|
||||||
&& self.right.is_constant()
|
&& self.right.is_constant()
|
||||||
&& !self.left.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;
|
&& 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) {
|
OPT(AST_SymbolExport, function(self) {
|
||||||
@@ -11593,9 +11611,7 @@ Compressor.prototype.compress = function(node) {
|
|||||||
if (lhs && is_atomic(lhs, self)) return self;
|
if (lhs && is_atomic(lhs, self)) return self;
|
||||||
return make_node(AST_UnaryPrefix, self, {
|
return make_node(AST_UnaryPrefix, self, {
|
||||||
operator: "void",
|
operator: "void",
|
||||||
expression: make_node(AST_Number, self, {
|
expression: make_node(AST_Number, self, { value: 0 }),
|
||||||
value: 0
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -11607,12 +11623,8 @@ Compressor.prototype.compress = function(node) {
|
|||||||
}
|
}
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "/",
|
operator: "/",
|
||||||
left: make_node(AST_Number, self, {
|
left: make_node(AST_Number, self, { value: 1 }),
|
||||||
value: 1
|
right: make_node(AST_Number, self, { value: 0 }),
|
||||||
}),
|
|
||||||
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;
|
if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "/",
|
operator: "/",
|
||||||
left: make_node(AST_Number, self, {
|
left: make_node(AST_Number, self, { value: 0 }),
|
||||||
value: 0
|
right: 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) {
|
OPT(AST_Boolean, function(self, compressor) {
|
||||||
if (!compressor.option("booleans")) return self;
|
if (!compressor.option("booleans")) return self;
|
||||||
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
|
if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value });
|
||||||
value: +self.value
|
|
||||||
});
|
|
||||||
var p = compressor.parent();
|
var p = compressor.parent();
|
||||||
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
|
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
|
||||||
AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|
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,
|
line : p.start.line,
|
||||||
col : p.start.col,
|
col : p.start.col,
|
||||||
});
|
});
|
||||||
return make_node(AST_Number, self, {
|
return make_node(AST_Number, self, { value: +self.value });
|
||||||
value: +self.value
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return make_node(AST_UnaryPrefix, self, {
|
return make_node(AST_UnaryPrefix, self, {
|
||||||
operator: "!",
|
operator: "!",
|
||||||
expression: make_node(AST_Number, self, {
|
expression: make_node(AST_Number, self, { value: 1 - self.value }),
|
||||||
value: 1 - self.value
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -508,7 +508,7 @@ drop_value: {
|
|||||||
((a, b) => a + b)(console.log(42));
|
((a, b) => a + b)(console.log(42));
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
((a, b) => {})(console.log(42));
|
void console.log(42);
|
||||||
}
|
}
|
||||||
expect_stdout: "42"
|
expect_stdout: "42"
|
||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
|
|||||||
@@ -890,7 +890,7 @@ keep_fnames: {
|
|||||||
issue_805_1: {
|
issue_805_1: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
passes: 2,
|
passes: 3,
|
||||||
pure_getters: "strict",
|
pure_getters: "strict",
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
sequences: true,
|
sequences: true,
|
||||||
@@ -926,7 +926,7 @@ issue_805_1: {
|
|||||||
issue_805_2: {
|
issue_805_2: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
passes: 2,
|
passes: 3,
|
||||||
pure_getters: "strict",
|
pure_getters: "strict",
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
sequences: true,
|
sequences: true,
|
||||||
|
|||||||
@@ -1765,7 +1765,7 @@ issue_2846: {
|
|||||||
issue_805_1: {
|
issue_805_1: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
passes: 2,
|
passes: 3,
|
||||||
pure_getters: "strict",
|
pure_getters: "strict",
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
sequences: true,
|
sequences: true,
|
||||||
@@ -1798,7 +1798,7 @@ issue_805_1: {
|
|||||||
issue_805_2: {
|
issue_805_2: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
passes: 2,
|
passes: 3,
|
||||||
pure_getters: "strict",
|
pure_getters: "strict",
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
sequences: true,
|
sequences: true,
|
||||||
|
|||||||
@@ -3862,6 +3862,7 @@ issue_3679_1: {
|
|||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
inline: true,
|
inline: true,
|
||||||
|
passes: 2,
|
||||||
pure_getters: "strict",
|
pure_getters: "strict",
|
||||||
reduce_vars: true,
|
reduce_vars: true,
|
||||||
side_effects: true,
|
side_effects: true,
|
||||||
|
|||||||
@@ -389,6 +389,7 @@ issue_1288_side_effects: {
|
|||||||
options = {
|
options = {
|
||||||
conditionals: true,
|
conditionals: true,
|
||||||
negate_iife: true,
|
negate_iife: true,
|
||||||
|
sequences: true,
|
||||||
side_effects: true,
|
side_effects: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
@@ -409,10 +410,10 @@ issue_1288_side_effects: {
|
|||||||
})(0);
|
})(0);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
w;
|
w,
|
||||||
x || function() {
|
x || function() {
|
||||||
x = {};
|
x = {};
|
||||||
}();
|
}(),
|
||||||
y;
|
y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -866,9 +866,9 @@ issue_4575: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
(function(a) {
|
(function(a) {
|
||||||
(function a(...d) {
|
(function(d) {
|
||||||
console.log(d.length);
|
console.log(d.length);
|
||||||
})();
|
})([]);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
expect_stdout: "0"
|
expect_stdout: "0"
|
||||||
|
|||||||
Reference in New Issue
Block a user