From 1858c2018c0e872e17e31599bd6bed5063322f28 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Thu, 31 Oct 2019 08:00:04 +0800 Subject: [PATCH] enhance `typeofs` (#3556) --- lib/compress.js | 79 +++++++++++++++++++++++----- test/compress/typeof.js | 114 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 13 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index a28c7a6c..9e2b6895 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1042,7 +1042,8 @@ merge(Compressor.prototype, { var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError"); AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { - return !this.definition().undeclared + return this.defined + || !this.definition().undeclared || compressor.option("unsafe") && global_names[this.name]; }); @@ -4570,6 +4571,49 @@ merge(Compressor.prototype, { return if_break_in_loop(self, compressor); }); + function mark_locally_defined(condition, consequent, alternative, operator) { + if (!(condition instanceof AST_Binary)) return; + if (!(condition.left instanceof AST_String)) { + if (!operator) operator = condition.operator; + if (condition.operator != operator) return; + switch (operator) { + case "&&": + case "||": + mark_locally_defined(condition.left, consequent, alternative, operator); + mark_locally_defined(condition.right, consequent, alternative, operator); + break; + } + return; + } + if (!(condition.right instanceof AST_UnaryPrefix)) return; + if (condition.right.operator != "typeof") return; + var sym = condition.right.expression; + if (!is_undeclared_ref(sym)) return; + var body; + var undef = condition.left.getValue() == "undefined"; + switch (condition.operator) { + case "==": + body = undef ? alternative : consequent; + break; + case "!=": + body = undef ? consequent : alternative; + break; + default: + return; + } + if (!body) return; + var def = sym.definition(); + var tw = new TreeWalker(function(node) { + if (node instanceof AST_Scope) { + var parent = tw.parent(); + if (parent instanceof AST_Call && parent.expression === node) return; + return true; + } + if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true; + }); + body.walk(tw); + } + OPT(AST_If, function(self, compressor) { if (is_empty(self.alternative)) self.alternative = null; @@ -4711,6 +4755,7 @@ merge(Compressor.prototype, { body: [ self, body ] }).optimize(compressor); } + if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative); return self; }); @@ -5698,7 +5743,7 @@ merge(Compressor.prototype, { // "undefined" == typeof x => undefined === x else if (compressor.option("typeofs") && self.left instanceof AST_String - && self.left.value == "undefined" + && self.left.getValue() == "undefined" && self.right instanceof AST_UnaryPrefix && self.right.operator == "typeof") { var expr = self.right.expression; @@ -6089,6 +6134,14 @@ merge(Compressor.prototype, { break; } } + if (compressor.option("typeofs")) switch (self.operator) { + case "&&": + mark_locally_defined(self.left, self.right, null, "&&"); + break; + case "||": + mark_locally_defined(self.left, null, self.right, "||"); + break; + } if (compressor.option("unsafe")) { var indexRight = is_indexFn(self.right); if (in_bool @@ -6651,8 +6704,8 @@ merge(Compressor.prototype, { }).optimize(compressor); } var in_bool = compressor.option("booleans") && compressor.in_boolean_context(); - if (is_true(self.consequent)) { - if (is_false(self.alternative)) { + if (is_true(consequent)) { + if (is_false(alternative)) { // c ? true : false ---> !!c return booleanize(condition); } @@ -6660,11 +6713,11 @@ merge(Compressor.prototype, { return make_node(AST_Binary, self, { operator: "||", left: booleanize(condition), - right: self.alternative + right: alternative }); } - if (is_false(self.consequent)) { - if (is_true(self.alternative)) { + if (is_false(consequent)) { + if (is_true(alternative)) { // c ? false : true ---> !c return booleanize(condition.negate(compressor)); } @@ -6672,26 +6725,26 @@ merge(Compressor.prototype, { return make_node(AST_Binary, self, { operator: "&&", left: booleanize(condition.negate(compressor)), - right: self.alternative + right: alternative }); } - if (is_true(self.alternative)) { + if (is_true(alternative)) { // c ? x : true ---> !c || x return make_node(AST_Binary, self, { operator: "||", left: booleanize(condition.negate(compressor)), - right: self.consequent + right: consequent }); } - if (is_false(self.alternative)) { + if (is_false(alternative)) { // c ? x : false ---> !!c && x return make_node(AST_Binary, self, { operator: "&&", left: booleanize(condition), - right: self.consequent + right: consequent }); } - + if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative); return self; function booleanize(node) { diff --git a/test/compress/typeof.js b/test/compress/typeof.js index 80123834..c3c2e9ec 100644 --- a/test/compress/typeof.js +++ b/test/compress/typeof.js @@ -295,3 +295,117 @@ issue_2728_6: { } expect_stdout: "function undefined" } + +typeof_defined_1: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "undefined" == typeof A && A; + "undefined" != typeof A && A; + "undefined" == typeof A || A; + "undefined" != typeof A || A; + } + expect: { + "undefined" == typeof A && A; + "undefined" != typeof A || A; + } +} + +typeof_defined_2: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "function" == typeof A && A; + "function" != typeof A && A; + "function" == typeof A || A; + "function" != typeof A || A; + } + expect: { + "function" != typeof A && A; + "function" == typeof A || A; + } +} + +typeof_defined_3: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && (A, B); + "undefined" != typeof A && "undefined" == typeof B && (A, B); + "undefined" != typeof A && "undefined" != typeof B && (A, B); + "undefined" == typeof A && "undefined" == typeof B || (A, B); + "undefined" == typeof A && "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B || (A, B); + "undefined" != typeof A && "undefined" != typeof B || (A, B); + "undefined" == typeof A || "undefined" == typeof B && (A, B); + "undefined" == typeof A || "undefined" != typeof B && (A, B); + "undefined" != typeof A || "undefined" == typeof B && (A, B); + "undefined" != typeof A || "undefined" != typeof B && (A, B); + "undefined" == typeof A || "undefined" == typeof B || (A, B); + "undefined" == typeof A || "undefined" != typeof B || (A, B); + "undefined" != typeof A || "undefined" == typeof B || (A, B); + "undefined" != typeof A || "undefined" != typeof B || (A, B); + } + expect: { + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && A; + "undefined" != typeof A && "undefined" == typeof B && B; + "undefined" == typeof A && "undefined" == typeof B || (A, B); + "undefined" == typeof A && "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B || (A, B); + "undefined" != typeof A && "undefined" != typeof B || (A, B); + "undefined" == typeof A || "undefined" == typeof B && B; + "undefined" != typeof A || "undefined" == typeof B && (A, B); + "undefined" != typeof A || "undefined" != typeof B && A; + "undefined" == typeof A || "undefined" != typeof B || B; + "undefined" != typeof A || "undefined" == typeof B || A; + "undefined" != typeof A || "undefined" != typeof B || (A, B); + } +} + +typeof_defined_4: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "object" == typeof A && "object" == typeof B && (A, B); + "object" == typeof A && "object" != typeof B && (A, B); + "object" != typeof A && "object" == typeof B && (A, B); + "object" != typeof A && "object" != typeof B && (A, B); + "object" == typeof A && "object" == typeof B || (A, B); + "object" == typeof A && "object" != typeof B || (A, B); + "object" != typeof A && "object" == typeof B || (A, B); + "object" != typeof A && "object" != typeof B || (A, B); + "object" == typeof A || "object" == typeof B && (A, B); + "object" == typeof A || "object" != typeof B && (A, B); + "object" != typeof A || "object" == typeof B && (A, B); + "object" != typeof A || "object" != typeof B && (A, B); + "object" == typeof A || "object" == typeof B || (A, B); + "object" == typeof A || "object" != typeof B || (A, B); + "object" != typeof A || "object" == typeof B || (A, B); + "object" != typeof A || "object" != typeof B || (A, B); + } + expect: { + "object" == typeof A && "object" != typeof B && B; + "object" != typeof A && "object" == typeof B && A; + "object" != typeof A && "object" != typeof B && (A, B); + "object" == typeof A && "object" == typeof B || (A, B); + "object" == typeof A && "object" != typeof B || (A, B); + "object" != typeof A && "object" == typeof B || (A, B); + "object" != typeof A && "object" != typeof B || (A, B); + "object" == typeof A || "object" == typeof B && A; + "object" == typeof A || "object" != typeof B && (A, B); + "object" != typeof A || "object" != typeof B && B; + "object" == typeof A || "object" == typeof B || (A, B); + "object" == typeof A || "object" != typeof B || A; + "object" != typeof A || "object" == typeof B || B; + } +} -- 2.34.1