diff --git a/lib/compress.js b/lib/compress.js index a28c7a6c11..9e2b689562 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 801238346f..c3c2e9ecf1 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; + } +}