enhance assignments & unused (#3428)

closes #3427
This commit is contained in:
Alex Lam S.L
2019-05-29 01:21:08 +08:00
committed by GitHub
parent e4f5ba1d29
commit 482e1baea3
4 changed files with 123 additions and 24 deletions

View File

@@ -2282,8 +2282,7 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor` // returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) { (function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) { AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
return !compressor.option("pure_getters") return !compressor.option("pure_getters") || this._dot_throw(compressor);
|| this._dot_throw(compressor);
}); });
function is_strict(compressor) { function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters")); return /strict/.test(compressor.option("pure_getters"));
@@ -2291,7 +2290,15 @@ merge(Compressor.prototype, {
def(AST_Node, is_strict); def(AST_Node, is_strict);
def(AST_Array, return_false); def(AST_Array, return_false);
def(AST_Assign, function(compressor) { def(AST_Assign, function(compressor) {
return this.operator == "=" && this.right._dot_throw(compressor); if (this.operator != "=") return false;
var rhs = this.right;
if (!rhs._dot_throw(compressor)) return false;
var sym = this.left;
if (!(sym instanceof AST_SymbolRef)) return true;
if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
return rhs.right._dot_throw(compressor);
}
return true;
}); });
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
@@ -3618,7 +3625,7 @@ merge(Compressor.prototype, {
var value = null; var value = null;
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) { if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = node.right; value = get_rhs(node);
} }
} else if (!in_use) { } else if (!in_use) {
value = make_node(AST_Number, node, { value = make_node(AST_Number, node, {
@@ -3843,6 +3850,15 @@ merge(Compressor.prototype, {
} }
} }
function get_rhs(assign) {
var rhs = assign.right;
if (!assign.write_only) return rhs;
if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
var sym = assign.left;
if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
return rhs.right;
}
function scan_ref_scoped(node, descend) { function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props); var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) { if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
@@ -3850,9 +3866,10 @@ merge(Compressor.prototype, {
prop.walk(tw); prop.walk(tw);
}); });
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
node.right.walk(tw); var right = get_rhs(node);
right.walk(tw);
if (node.left === sym) { if (node.left === sym) {
if (!node_def.chained && sym.fixed_value(true) === node.right) { if (!node_def.chained && sym.fixed_value(true) === right) {
fixed_ids[node_def.id] = node; fixed_ids[node_def.id] = node;
} }
if (!node.write_only) { if (!node.write_only) {
@@ -4163,12 +4180,14 @@ merge(Compressor.prototype, {
}); });
def(AST_Assign, function(compressor) { def(AST_Assign, function(compressor) {
var left = this.left; var left = this.left;
if (left.has_side_effects(compressor) if (left instanceof AST_PropAccess) {
|| compressor.has_directive("use strict") var expr = left.expression;
&& left instanceof AST_PropAccess if (expr instanceof AST_Assign && !expr.may_throw_on_access(compressor)) {
&& left.expression.is_constant()) { expr.write_only = true;
return this;
} }
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
this.write_only = true; this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) { if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor); return this.right.drop_side_effect_free(compressor);
@@ -4243,8 +4262,9 @@ merge(Compressor.prototype, {
}); });
def(AST_Constant, return_null); def(AST_Constant, return_null);
def(AST_Dot, function(compressor, first_in_statement) { def(AST_Dot, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(compressor)) return this; var expr = this.expression;
return this.expression.drop_side_effect_free(compressor, first_in_statement); if (expr.may_throw_on_access(compressor)) return this;
return expr.drop_side_effect_free(compressor, first_in_statement);
}); });
def(AST_Function, function(compressor) { def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null; return this.name && compressor.option("ie8") ? this : null;
@@ -5556,20 +5576,29 @@ merge(Compressor.prototype, {
self.right = tmp; self.right = tmp;
} }
} }
if (commutativeOperators[self.operator]) { if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
if (self.right.is_constant()
&& !self.left.is_constant()) {
// if right is a constant, whatever side effects the // if right is a constant, whatever side effects the
// left side might have could not influence the // left side might have could not influence the
// result. hence, force switch. // result. hence, force switch.
if (!(self.left instanceof AST_Binary if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse(); reverse();
} }
} }
}
self = self.lift_sequences(compressor); self = self.lift_sequences(compressor);
if (compressor.option("assignments") && lazy_op[self.operator]) {
var assign = self.right;
// a || (a = x) => a = a || x
// a && (a = x) => a = a && x
if (self.left instanceof AST_SymbolRef
&& assign instanceof AST_Assign
&& assign.operator == "="
&& self.left.equivalent_to(assign.left)) {
self.right = assign.right;
assign.right = self;
return assign;
}
}
if (compressor.option("comparisons")) switch (self.operator) { if (compressor.option("comparisons")) switch (self.operator) {
case "===": case "===":
case "!==": case "!==":

View File

@@ -311,3 +311,19 @@ issue_3375: {
} }
expect_stdout: "string" expect_stdout: "string"
} }
issue_3427: {
options = {
assignments: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a || (a = {});
})();
}
expect: {}
}

View File

@@ -2028,3 +2028,37 @@ issue_3375: {
} }
expect_stdout: "0 0" expect_stdout: "0 0"
} }
issue_3427_1: {
options = {
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a = a || {};
})();
}
expect: {}
}
issue_3427_2: {
options = {
unused: true,
}
input: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect_stdout: "PASS"
}

View File

@@ -1187,3 +1187,23 @@ drop_arguments: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(function(b) {
b.p = 42;
})(a || (a = {}));
}
expect: {}
}