aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2019-05-29 01:21:08 +0800
committerGitHub <noreply@github.com>2019-05-29 01:21:08 +0800
commit482e1baea3672fc1bc39594a5615487d05a15f5f (patch)
tree4b82778d94b722624446d529502f01f88aca41ee
parente4f5ba1d294c7e22c503dc29c141a6613fbb0142 (diff)
downloadtracifyjs-482e1baea3672fc1bc39594a5615487d05a15f5f.tar.gz
tracifyjs-482e1baea3672fc1bc39594a5615487d05a15f5f.zip
enhance `assignments` & `unused` (#3428)
closes #3427
-rw-r--r--lib/compress.js77
-rw-r--r--test/compress/assignment.js16
-rw-r--r--test/compress/drop-unused.js34
-rw-r--r--test/compress/pure_getters.js20
4 files changed, 123 insertions, 24 deletions
diff --git a/lib/compress.js b/lib/compress.js
index e99ce508..7bbb5a20 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -2282,8 +2282,7 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
- return !compressor.option("pure_getters")
- || this._dot_throw(compressor);
+ return !compressor.option("pure_getters") || this._dot_throw(compressor);
});
function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters"));
@@ -2291,7 +2290,15 @@ merge(Compressor.prototype, {
def(AST_Node, is_strict);
def(AST_Array, return_false);
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) {
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;
if (node instanceof AST_Assign) {
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) {
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) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
@@ -3850,9 +3866,10 @@ merge(Compressor.prototype, {
prop.walk(tw);
});
if (node instanceof AST_Assign) {
- node.right.walk(tw);
+ var right = get_rhs(node);
+ right.walk(tw);
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;
}
if (!node.write_only) {
@@ -4163,12 +4180,14 @@ merge(Compressor.prototype, {
});
def(AST_Assign, function(compressor) {
var left = this.left;
- if (left.has_side_effects(compressor)
- || compressor.has_directive("use strict")
- && left instanceof AST_PropAccess
- && left.expression.is_constant()) {
- return this;
+ if (left instanceof AST_PropAccess) {
+ var expr = left.expression;
+ if (expr instanceof AST_Assign && !expr.may_throw_on_access(compressor)) {
+ expr.write_only = true;
+ }
+ if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
+ if (left.has_side_effects(compressor)) return this;
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
@@ -4243,8 +4262,9 @@ merge(Compressor.prototype, {
});
def(AST_Constant, return_null);
def(AST_Dot, function(compressor, first_in_statement) {
- if (this.expression.may_throw_on_access(compressor)) return this;
- return this.expression.drop_side_effect_free(compressor, first_in_statement);
+ var expr = this.expression;
+ if (expr.may_throw_on_access(compressor)) return this;
+ return expr.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null;
@@ -5556,20 +5576,29 @@ merge(Compressor.prototype, {
self.right = tmp;
}
}
- if (commutativeOperators[self.operator]) {
- 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.
-
- if (!(self.left instanceof AST_Binary
- && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
- reverse();
- }
+ if (commutativeOperators[self.operator] && 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.
+ if (!(self.left instanceof AST_Binary
+ && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
+ reverse();
}
}
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) {
case "===":
case "!==":
diff --git a/test/compress/assignment.js b/test/compress/assignment.js
index ece0185d..ecd0ac4a 100644
--- a/test/compress/assignment.js
+++ b/test/compress/assignment.js
@@ -311,3 +311,19 @@ issue_3375: {
}
expect_stdout: "string"
}
+
+issue_3427: {
+ options = {
+ assignments: true,
+ sequences: true,
+ side_effects: true,
+ unused: true,
+ }
+ input: {
+ (function() {
+ var a;
+ a || (a = {});
+ })();
+ }
+ expect: {}
+}
diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js
index d7f3cf76..6ed193e9 100644
--- a/test/compress/drop-unused.js
+++ b/test/compress/drop-unused.js
@@ -2028,3 +2028,37 @@ issue_3375: {
}
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"
+}
diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js
index b6e42f9d..54c89c6d 100644
--- a/test/compress/pure_getters.js
+++ b/test/compress/pure_getters.js
@@ -1187,3 +1187,23 @@ drop_arguments: {
}
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: {}
+}