From 6fe20dbe33bc78b161489207287c9154d5d32fd4 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 24 Apr 2019 21:38:55 +0800 Subject: [PATCH] enhance `comparisons` (#3379) --- lib/compress.js | 69 +++++++++++++++++++++++++++++++----- test/compress/comparisons.js | 20 +++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 9430c15d..767f3dcc 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2296,17 +2296,14 @@ 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); - }) + return this.operator == "=" && this.right._dot_throw(compressor); + }); def(AST_Binary, function(compressor) { - return (this.operator == "&&" || 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)); + }); def(AST_Conditional, function(compressor) { - return this.consequent._dot_throw(compressor) - || this.alternative._dot_throw(compressor); - }) + return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor); + }); def(AST_Constant, return_false); def(AST_Dot, function(compressor) { if (!is_strict(compressor)) return false; @@ -2346,6 +2343,51 @@ merge(Compressor.prototype, { node.DEFMETHOD("_dot_throw", func); }); + (function(def) { + def(AST_Node, return_false); + def(AST_Array, return_true); + def(AST_Assign, function(compressor) { + return this.operator != "=" || this.right.is_defined(compressor); + }); + def(AST_Binary, function(compressor) { + switch (this.operator) { + case "&&": + return this.left.is_defined(compressor) && this.right.is_defined(compressor); + case "||": + return this.left.is_defined(compressor) || this.right.is_defined(compressor); + default: + return true; + } + }); + def(AST_Conditional, function(compressor) { + return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor); + }); + def(AST_Constant, return_true); + def(AST_Lambda, return_true); + def(AST_Object, return_true); + def(AST_Sequence, function(compressor) { + return this.tail_node().is_defined(compressor); + }); + def(AST_SymbolRef, function(compressor) { + if (this.is_undefined) return false; + if (is_undeclared_ref(this) && this.is_declared(compressor)) return true; + if (this.is_immutable()) return true; + var fixed = this.fixed_value(); + if (!fixed) return false; + this.is_defined = return_true; + var result = fixed.is_defined(compressor); + delete this.is_defined; + return result; + }); + def(AST_UnaryPrefix, function() { + return this.operator != "void"; + }); + def(AST_UnaryPostfix, return_true); + def(AST_Undefined, return_false); + })(function(node, func) { + node.DEFMETHOD("is_defined", func); + }); + /* -----[ boolean/negation helpers ]----- */ // methods to determine whether an expression has a boolean result type @@ -5490,6 +5532,13 @@ merge(Compressor.prototype, { if (compressor.option("comparisons")) switch (self.operator) { case "===": case "!==": + if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) { + compressor.warn("Expression always defined [{file}:{line},{col}]", self.start); + return make_sequence(self, [ + self.right, + make_node(self.operator == "===" ? AST_False : AST_True, self) + ]).optimize(compressor); + } var is_strict_comparison = true; if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || (self.left.is_number(compressor) && self.right.is_number(compressor)) || @@ -5528,6 +5577,8 @@ merge(Compressor.prototype, { break; case "&&": case "||": + // void 0 !== x && null !== x => null != x + // void 0 === x || null === x => null == x var lhs = self.left; if (lhs.operator == self.operator) { lhs = lhs.right; diff --git a/test/compress/comparisons.js b/test/compress/comparisons.js index 434f09c9..46805625 100644 --- a/test/compress/comparisons.js +++ b/test/compress/comparisons.js @@ -345,3 +345,23 @@ is_boolean_var: { } expect_stdout: "1" } + +is_defined: { + options = { + comparisons: true, + } + input: { + console.log(function a() { + return void 0 === a; + }()); + } + expect: { + console.log(function a() { + return a, false; + }()); + } + expect_stdout: "false" + expect_warnings: [ + "WARN: Expression always defined [test/compress/comparisons.js:2,19]", + ] +} -- 2.34.1