aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2018-03-16 06:12:59 +0800
committerGitHub <noreply@github.com>2018-03-16 06:12:59 +0800
commit20ca0f5906c68df11d2f44e8b381212672afc6e1 (patch)
tree2a2fd0feb19255c26d3cd8167776804d10beb04c
parentb29d435bb5bc4d883a23efaabd76e95092352b6c (diff)
downloadtracifyjs-20ca0f5906c68df11d2f44e8b381212672afc6e1.tar.gz
tracifyjs-20ca0f5906c68df11d2f44e8b381212672afc6e1.zip
improve truthy compression (#3009)
-rw-r--r--lib/compress.js92
-rw-r--r--test/compress/conditionals.js7
-rw-r--r--test/compress/evaluate.js42
-rw-r--r--test/compress/issue-1261.js2
-rw-r--r--test/compress/negate-iife.js4
-rw-r--r--test/compress/reduce_vars.js27
-rw-r--r--test/compress/transform.js2
7 files changed, 127 insertions, 49 deletions
diff --git a/lib/compress.js b/lib/compress.js
index cacffd37..39220d68 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -2041,6 +2041,28 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor);
}
+ // is_truthy()
+ // return true if `!!node === true`
+ (function(def) {
+ def(AST_Node, return_false);
+ def(AST_Array, return_true);
+ def(AST_Assign, function() {
+ return this.operator == "=" && this.right.is_truthy();
+ });
+ def(AST_Lambda, return_true);
+ def(AST_Object, return_true);
+ def(AST_RegExp, return_true);
+ def(AST_Sequence, function() {
+ return this.tail_node().is_truthy();
+ });
+ def(AST_SymbolRef, function() {
+ var fixed = this.fixed_value();
+ return fixed && fixed.is_truthy();
+ });
+ })(function(node, func) {
+ node.DEFMETHOD("is_truthy", func);
+ });
+
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
@@ -3821,7 +3843,7 @@ merge(Compressor.prototype, {
OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self;
- var cond = self.condition.tail_node().evaluate(compressor);
+ var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) {
if (cond) return make_node(AST_For, self, {
body: make_node(AST_BlockStatement, self.body, {
@@ -3943,9 +3965,11 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
}
- if (compressor.option("dead_code")) {
- if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
- if (!cond) {
+ if (cond instanceof AST_Node) {
+ cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+ }
+ if (!cond) {
+ if (compressor.option("dead_code")) {
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) {
@@ -3960,6 +3984,16 @@ merge(Compressor.prototype, {
}));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
+ } else if (self.condition && !(cond instanceof AST_Node)) {
+ self.body = make_node(AST_BlockStatement, self.body, {
+ body: [
+ make_node(AST_SimpleStatement, self.condition, {
+ body: self.condition
+ }),
+ self.body
+ ]
+ });
+ self.condition = null;
}
}
return if_break_in_loop(self, compressor);
@@ -3980,7 +4014,9 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
if (compressor.option("dead_code")) {
- if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
+ if (cond instanceof AST_Node) {
+ cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+ }
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
var body = [];
@@ -4864,8 +4900,10 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
- if (compressor.in_boolean_context()) {
- switch (self.operator) {
+ if (compressor.option("booleans")) {
+ if (self.operator == "!" && e.is_truthy()) {
+ return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
+ } else if (compressor.in_boolean_context()) switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
@@ -5106,7 +5144,7 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) {
switch (self.operator) {
case "&&":
- var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
+ var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
@@ -5141,7 +5179,7 @@ merge(Compressor.prototype, {
}
break;
case "||":
- var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
+ var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -5379,6 +5417,13 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self);
}
return self;
+
+ function fuzzy_eval(node) {
+ if (node.truthy) return true;
+ if (node.falsy) return false;
+ if (node.is_truthy()) return true;
+ return node.evaluate(compressor);
+ }
});
function recursive_ref(compressor, def) {
@@ -5674,15 +5719,13 @@ merge(Compressor.prototype, {
expressions.push(self);
return make_sequence(self, expressions);
}
- var cond = self.condition.evaluate(compressor);
- if (cond !== self.condition) {
- if (cond) {
- compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
- return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
- } else {
- compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
- return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
- }
+ var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
+ if (!cond) {
+ compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
+ return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
+ } else if (!(cond instanceof AST_Node)) {
+ compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
+ return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
}
var negated = cond.negate(compressor, first_in_statement(compressor));
if (best_of(compressor, cond, negated) === negated) {
@@ -6122,19 +6165,6 @@ merge(Compressor.prototype, {
return self;
});
- function literals_in_boolean_context(self, compressor) {
- if (compressor.in_boolean_context()) {
- return best_of(compressor, self, make_sequence(self, [
- self,
- make_node(AST_True, self)
- ]).optimize(compressor));
- }
- return self;
- };
- OPT(AST_Array, literals_in_boolean_context);
- OPT(AST_Object, literals_in_boolean_context);
- OPT(AST_RegExp, literals_in_boolean_context);
-
OPT(AST_Return, function(self, compressor){
if (self.value && is_undefined(self.value, compressor)) {
self.value = null;
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index abb39697..03386d18 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -703,10 +703,11 @@ ternary_boolean_alternative: {
trivial_boolean_ternary_expressions : {
options = {
+ booleans: true,
conditionals: true,
- evaluate : true,
- booleans : true
- };
+ evaluate: true,
+ side_effects: true,
+ }
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 0547e6d9..9d35ffa3 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -745,7 +745,7 @@ in_boolean_context: {
!b("foo"),
!b([1, 2]),
!b(/foo/),
- ![1, foo()],
+ (foo(), !1),
(foo(), !1)
);
}
@@ -1566,3 +1566,43 @@ issue_2968: {
}
expect_stdout: "PASS"
}
+
+truthy_conditionals: {
+ options = {
+ conditionals: true,
+ evaluate: true,
+ }
+ input: {
+ if (a = {}) x();
+ (b = /foo/) && y();
+ (c = function() {}) || z();
+ }
+ expect: {
+ a = {}, x();
+ b = /foo/, y();
+ c = function() {};
+ }
+}
+
+truthy_loops: {
+ options = {
+ evaluate: true,
+ loops: true,
+ }
+ input: {
+ while ([]) x();
+ do {
+ y();
+ } while(a = {});
+ }
+ expect: {
+ for (;;) {
+ [];
+ x();
+ }
+ for (;;) {
+ y();
+ a = {};
+ }
+ }
+}
diff --git a/test/compress/issue-1261.js b/test/compress/issue-1261.js
index 9f4f466f..888fde40 100644
--- a/test/compress/issue-1261.js
+++ b/test/compress/issue-1261.js
@@ -175,8 +175,8 @@ should_warn: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
"WARN: Condition always true [test/compress/issue-1261.js:141,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
- "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
+ "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
"WARN: Condition always false [test/compress/issue-1261.js:144,8]",
]
diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js
index 66d24270..17148cad 100644
--- a/test/compress/negate-iife.js
+++ b/test/compress/negate-iife.js
@@ -67,7 +67,7 @@ negate_iife_3_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
- console.log(true);
+ true, console.log(true);
}
expect_stdout: true
}
@@ -110,7 +110,7 @@ negate_iife_3_off_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
- console.log(true);
+ true, console.log(true);
}
expect_stdout: true
}
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 815dff32..3d11ba42 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -55,7 +55,7 @@ reduce_vars: {
console.log(a - 5);
eval("console.log(a);");
})(eval);
- "yes";
+ true, "yes";
console.log(A + 1);
}
expect_stdout: true
@@ -147,7 +147,7 @@ modified: {
}
function f4() {
var b = 2, c = 3;
- b = c;
+ 1, b = c;
console.log(1 + b);
console.log(b + c);
console.log(1 + c);
@@ -715,10 +715,12 @@ passes: {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
+ sequences: true,
+ side_effects: true,
unused: true,
}
input: {
- function f() {
+ (function() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
@@ -729,17 +731,22 @@ passes: {
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
- }
+ })();
}
expect: {
- function f() {
- 3;
- console.log(4);
- console.log(6);
- console.log(4);
+ (function() {
+ console.log(4),
+ console.log(6),
+ console.log(4),
console.log(7);
- }
+ })();
}
+ expect_stdout: [
+ "4",
+ "6",
+ "4",
+ "7",
+ ]
}
iife: {
diff --git a/test/compress/transform.js b/test/compress/transform.js
index 58874cd6..867b8ade 100644
--- a/test/compress/transform.js
+++ b/test/compress/transform.js
@@ -59,7 +59,7 @@ if_else_empty: {
if ({} ? a : b); else {}
}
expect: {
- !{} ? b : a;
+ ({}), a;
}
}