fix corner case in `typeofs` (#3818)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 24 Apr 2020 17:29:42 +0000 (18:29 +0100)
committerGitHub <noreply@github.com>
Fri, 24 Apr 2020 17:29:42 +0000 (01:29 +0800)
fixes #3817

lib/compress.js
test/compress/typeof.js

index 9420d8d..3d1b17f 100644 (file)
@@ -5194,16 +5194,17 @@ merge(Compressor.prototype, {
         return if_break_in_loop(self, compressor);
     });
 
-    function mark_locally_defined(condition, consequent, alternative, operator) {
+    function mark_locally_defined(condition, consequent, alternative) {
         if (!(condition instanceof AST_Binary)) return;
         if (!(condition.left instanceof AST_String)) {
-            if (!operator) operator = condition.operator;
-            if (condition.operator != operator) return;
-            switch (operator) {
+            switch (condition.operator) {
               case "&&":
+                mark_locally_defined(condition.left, consequent);
+                mark_locally_defined(condition.right, consequent);
+                break;
               case "||":
-                mark_locally_defined(condition.left, consequent, alternative, operator);
-                mark_locally_defined(condition.right, consequent, alternative, operator);
+                mark_locally_defined(negate(condition.left), alternative);
+                mark_locally_defined(negate(condition.right), alternative);
                 break;
             }
             return;
@@ -5235,6 +5236,20 @@ merge(Compressor.prototype, {
             if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
         });
         body.walk(tw);
+
+        function negate(node) {
+            if (!(node instanceof AST_Binary)) return;
+            switch (node.operator) {
+              case "==":
+                node = node.clone();
+                node.operator = "!=";
+                return node;
+              case "!=":
+                node = node.clone();
+                node.operator = "==";
+                return node;
+            }
+        }
     }
 
     OPT(AST_If, function(self, compressor) {
@@ -6623,7 +6638,7 @@ merge(Compressor.prototype, {
                 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
                 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
                 self.left.equivalent_to(self.right)) {
-                self.operator = self.operator.substr(0, 2);
+                self.operator = self.operator.slice(0, 2);
             }
             // XXX: intentionally falling down to the next case
           case "==":
@@ -7102,10 +7117,10 @@ merge(Compressor.prototype, {
         }
         if (compressor.option("typeofs")) switch (self.operator) {
           case "&&":
-            mark_locally_defined(self.left, self.right, null, "&&");
+            mark_locally_defined(self.left, self.right, null);
             break;
           case "||":
-            mark_locally_defined(self.left, null, self.right, "||");
+            mark_locally_defined(self.left, null, self.right);
             break;
         }
         if (compressor.option("unsafe")) {
index aae7405..320817e 100644 (file)
@@ -435,3 +435,25 @@ emberjs_global: {
     }
     expect_stdout: Error("PASS")
 }
+
+issue_3817: {
+    options = {
+        comparisons: true,
+        conditionals: true,
+        passes: 2,
+        typeofs: true,
+    }
+    input: {
+        if ("A" == typeof A || !console.log("PASS")) switch (false) {
+          case "undefined" == typeof A:
+            console.log("FAIL");
+        }
+    }
+    expect: {
+        if ("A" == typeof A || !console.log("PASS")) switch (false) {
+          case "undefined" == typeof A:
+            console.log("FAIL");
+        }
+    }
+    expect_stdout: "PASS"
+}