Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(bar_2) {
    var a_1 = (c = c + 1) + [ --b + /[abc4]/g.exec(([ (c = c + 1) + (a_1 >>= (c = c + 1) + 38..toString()) ] || b || 5).toString()), --b + (typeof f2 == "function" && --_calls_ >= 0 && f2("c", [], "function")), (c = c + 1) + (/[abc4]/g.exec((bar_2 || b || 5).toString()) ? -5 : (c = c + 1) + a++), {}[{
        static: --b + {
            undefined: a++ + (undefined in [ (c = 1 + c, a_1 += (2 > NaN) / ("foo", [ , 0 ][1]) & ("foo" >= [] ^ this * -3)), (c = 1 + c, 
            (null ^ false) !== -3 - 24..toString() & 3 >> "bar" << Number(0xdeadn << 16n | 0xbeefn) + -4) ]),
            bar_2: bar_2
        }[typeof f1 == "function" && --_calls_ >= 0 && f1()],
        next: typeof f2 == "function" && --_calls_ >= 0 && f2(b *= a, a++ + [])
    }], (c = c + 1) + !function() {
        switch ((c = c + 1) + +b) {
          case true in {
                in: ((c = 1 + c, ((38..toString() || undefined) && [ , 0 ][1] < -0) !== (undefined >= (-42n).toString()) + ({} || "foo")) || a || 3).toString(),
                [a++ + a_1]: a++ + (a_1 = (c = 1 + c, (bar_2 && (bar_2.NaN >>= "" ^ 23..toString() ^ 4 & 5)) * ((-0 >= -3) >> (true >= -5)))),
                3: bar_2 = (c = 1 + c, ((bar_2 &&= 22 & 38..toString()) === void null) >= ((22 ^ undefined) > 24..toString() % 23..toString()))
            }:
            break;

          default:
            for (var brake3 = 5; (c = c + 1) + (1 === 1 ? a : b) && brake3 > 0; --brake3) {
                c = 1 + c, [] < this <= (4 > 3) !== (("undefined" & 38..toString()) == "function" / !0o644n);
            }
            {
                var expr5 = [ (c = 1 + c, ([ 3n ][0] > 2 === -0) >= ("object" > undefined) !== ("bar" <= false || -5 - 1)) ];
                L11377: for (var key5 of expr5) {
                    {
                    }
                }
            }

          case --b + (typeof a_1 == "function" && --_calls_ >= 0 && a_1(-5, 4, (c = c + 1) + a_1)):
            {
                var expr7 = typeof a_1 == "function" && --_calls_ >= 0 && a_1("number");
                for (var key7 in expr7) {
                }
            }
            break;

          case --b + {
                foo: {
                    undefined: (c = 1 + c, (a_1 && (a_1[typeof f2 == "function" && --_calls_ >= 0 && f2((c = 1 + c, 
                    (bar_2 = "" / "object" * (a_1 && (a_1.a += !0o644n - 2))) ^ delete NaN - /[a2][^e]+$/ / NaN))] = "function" >>> "bar" | this ^ !0o644n)) >> (a_1 = 0 & 23..toString()) % ([ , 0 ][1] || "")),
                    foo: (c = 1 + c, (Number(0xdeadn << 16n | 0xbeefn) === "a") < (a_1 && (a_1.b >>= 23..toString() | 24..toString())) !== (c = c + 1, 
                    24..toString()) >= (22 > 0))
                }.undefined,
                set "\t"(a_1_2) {
                    c = 1 + c, (0 | 5, false + "bar") != "a" % 22 << (a_1_2 += "b" % true);
                    this.done ^= /[a2][^e]+$/ / "number" != (this, "b");
                },
                "-2": {}.Infinity,
                3: --b + [ (c = 1 + c, (([] ^ NaN) << (23..toString() >>> -3)) / (+ -3 | "undefined" !== 25)), (c = 1 + c, 
                ((-42n).toString() || 3) + (bar_2 && (bar_2[c = 1 + c, (bar_2 && (bar_2[-0 ^ "bar" ^ (c = c + 1, 
                5) || -0 << -4 & [ , 0 ].length === 2 != this] ??= 4 * 24..toString() <= (1 == "function"))) + (("bar" || 23..toString()) >>> ("number" > "object"))] = "c" >= this)) >>> (([ 3n ][0] > 2) >>> 23..toString()) + Number(0xdeadn << 16n | 0xbeefn) * -0), (c = 1 + c, 
                c = c + 1, 38..toString() >> "" ^ (bar_2 ||= 22 !== 2)), (c = 1 + c, ("foo" <= -2) >>> [ , 0 ][1] + [ , 0 ][1] >> (([ , 0 ].length === 2) / false & (0 && [ , 0 ][1]))) ][c = 1 + c, 
                "function" * "bar" < 22 >> 3 >= (1 != 0) << (-2 ^ undefined)],
                [(c = c + 1) + /[abc4]/g.exec(((c = 1 + c, (-0 << 3 == "b" >= -4) % (4 > -0 <= -0 << "undefined")) || b || 5).toString())]: b = a
            }[a_1]:
            for (var brake10 = 5; ~((a_1 -= "object" !== -5) >> -1 - "undefined" == (3 && "object", 
            "" + undefined)) && brake10 > 0; --brake10) {
            }
            break;
        }
        {
            var brake12 = 5;
            L11378: while ((a++ + (/[a2][^e]+$/ * -2 << (5 >> ([ 3n ][0] > 2)) | 24..toString() < ([ , 0 ].length === 2) == ("bar" | 4)) ? bar_2 && typeof bar_2.in == "function" && --_calls_ >= 0 && (--b + (typeof f1 == "function" && --_calls_ >= 0 && f1()), 
            bar_2.in)((c = 1 + c, a_1 && (a_1.foo = (1 | 25 || 38..toString() >>> [ , 0 ][1]) !== undefined > "a" < (bar_2 = null >>> -2))), !0o644n) : a++ + []) && --brake12 > 0) {
                var bar_2 = --b + /[abc4]/g.exec(("foo" in {
                    [(c = 1 + c, 4 != undefined == {} % "" === ("" || 23..toString()) << ([ 3n ][0] > 2 == (-42n).toString()))]: (c = 1 + c, 
                    ((bar_2 && (bar_2[c = 1 + c, (38..toString() < ([ 3n ][0] > 2) ^ (a_2 && (a_2[c = 1 + c, 
                    delete ("number" == "number") - (("foo" ^ 24..toString()) >= [ , 0 ][1] % 1)] += 5 === "undefined"))) - (a_2 |= (a_1 && (a_1[c = 1 + c, 
                    (this | "function") * ("" * ([ , 0 ].length === 2)) === ([ 3n ][0] > 2) * "a" * (23..toString() ^ 23..toString())] = 23..toString() % "c")) > {} + ([ , 0 ].length === 2))] = "" ^ (-42n).toString())) ^ 0 >= 5) == (a_2 /= "" - 25 ^ 3 * "c"))
                } || b || 5).toString()), a_2 = ++a;
            }
        }
        a++ + (typeof bar_2 == "function" && --_calls_ >= 0 && bar_2(null, "bar"));
    }() ];
    (c = c + 1) + b--;
}

var b_1 = f0(23..toString(), --b + (--b + (a && a.NaN)));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(bar_2) {
    var a_1 = (c += 1) + [ --b + /[abc4]/g.exec([ (c += 1) + (a_1 >>= (c += 1) + 38..toString()) ].toString()), --b + ("function" == typeof f2 && 0 <= --_calls_ && f2("c", [], "function")), (c += 1) + (/[abc4]/g.exec((bar_2 || b || 5).toString()) ? -5 : (c += 1) + a++), {}[{
        static: --b + {
            undefined: a++ + (void 0 in [ a_1 += NaN & ([] <= "foo" ^ -3 * this), (c = 1 + (c = 1 + c), 
            0 != -3 - 24..toString() & 3 << Number(0xdeadn << 16n | 0xbeefn) + -4) ]),
            bar_2: bar_2
        }["function" == typeof f1 && 0 <= --_calls_ && f1()],
        next: "function" == typeof f2 && 0 <= --_calls_ && f2(b *= a, a++ + [])
    }], (c += 1) + !function() {
        switch ((c += 1) + +b) {
          case !0 in {
                in: (c = 1 + c, ((!38..toString() && void 0) !== ((-42n).toString() <= void 0) + {} || a || 3).toString()),
                [a++ + a_1]: a++ + (c = 1 + c, a_1 = 0 * (bar_2 && (bar_2.NaN >>= 4 ^ 23..toString()))),
                3: (c = 1 + c, bar_2 &&= 22 & 38..toString(), bar_2 = 24..toString() % 23..toString() < 22 <= !1)
            }:
            break;

          default:
            for (var key5, brake3 = 5; (c += 1) + a && 0 < brake3; --brake3) {
                c = 1 + c, 38..toString();
            }
            for (key5 of [ (c = 1 + c, !1 <= (2 < 3n === -0) !== -6) ]) {}

          case --b + ("function" == typeof a_1 && 0 <= --_calls_ && a_1(-5, 4, (c += 1) + a_1)):
            "function" == typeof a_1 && 0 <= --_calls_ && a_1("number");
            break;

          case --b + {
                foo: [ (c = 1 + c, (a_1 && (a_1["function" == typeof f2 && 0 <= --_calls_ && f2((c = 1 + c, 
                (bar_2 = NaN * (a_1 && (a_1.a += !0o644n - 2))) ^ delete NaN - NaN))] = 0 | !0o644n ^ this)) >> (a_1 = 0 & 23..toString()) % ""), (c = 1 + c, 
                Number(0xdeadn << 16n | 0xbeefn), a_1 && (a_1.b >>= 23..toString() | 24..toString()), 
                c += 1, 24..toString()) ][0],
                set "\t"(a_1_2) {
                    c = 1 + c, this.done ^= !0;
                },
                "-2": {}.Infinity,
                3: --b + [ (c = 1 + c, ((NaN ^ []) << (23..toString() >>> -3)) / -3), (c = 1 + c, 
                ((-42n).toString() || 3) + (bar_2 && (bar_2[c = 1 + c, (bar_2 && (bar_2[0 ^ (c += 1, 
                5)] ??= 4 * 24..toString() <= !1)) + 0] = this <= "c")) >>> ((2 < 3n) >>> 23..toString()) + -0 * Number(0xdeadn << 16n | 0xbeefn)), (c = 1 + c, 
                c += 1, 38..toString() >> "" ^ (bar_2 ||= !0)), 0 >> ((2 === [ , 0 ].length) / !1 & 0) ][c = 1 + (c = 1 + c), 
                !1],
                [(c += 1) + /[abc4]/g.exec((c = 1 + c, (b || 5).toString()))]: b = a
            }[a_1]:
            for (var brake10 = 5; ~((a_1 -= !0) >> NaN == "" + void 0) && 0 < brake10; --brake10) {}
        }
        for (var brake12 = 5; (a++ + (NaN << (5 >> (2 < 3n)) | 24..toString() < (2 === [ , 0 ].length) == 4) ? bar_2 && "function" == typeof bar_2.in && 0 <= --_calls_ && (--b, 
        "function" == typeof f1 && 0 <= --_calls_ && f1(), bar_2.in)((c = 1 + c, a_1 && (a_1.foo = 25 !== !1 < (bar_2 = 0))), !0o644n) : a++ + []) && 0 < --brake12; ) {
            var bar_2 = (--b + /[abc4]/g.exec(("foo" in {
                [(c = 1 + c, 1 == {} % "" === 23..toString() << (2 < 3n == (-42n).toString()))]: (c = 1 + c, 
                (!1 ^ (bar_2 && (bar_2[c = 1 + c, (38..toString() < (2 < 3n) ^ (a_2 && (a_2[c = 1 + c, 
                !0 - (0 <= ("foo" ^ 24..toString()))] += !1))) - (a_2 |= (a_1 && (a_1[c = 1 + c, 
                "" * (2 === [ , 0 ].length) * ("function" | this) == "a" * (2 < 3n) * (23..toString() ^ 23..toString())] = 23..toString() % "c")) > {} + (2 === [ , 0 ].length))] = "" ^ (-42n).toString()))) == (a_2 /= -25))
            } || b || 5).toString())), a_2 = ++a;
        }
        a++, "function" == typeof bar_2 && 0 <= --_calls_ && bar_2(null, "bar");
    }() ];
    c += 1, b--;
}

var b_1 = f0(23..toString(), --b + (--b + (a && a.NaN)));

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
null 106 103 38 Infinity NaN undefined

uglified result:
null 106 103 36 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
console.log((bar_2 &&= 0) === void 0);

var bar_2 = 0;
// output: true
// 
// minify: false
// 
// options: {
//   "mangle": false,
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false,
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  comparisons
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index 6db9518729b..cc67bb2bc69 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -3689,20 +3689,25 @@ merge(Compressor.prototype, {
     (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) {
+        function is_binary_defined(compressor, op, node) {
+            switch (op) {
               case "&&":
-                return this.left.is_defined(compressor) && this.right.is_defined(compressor);
+                return node.left.is_defined(compressor) && node.right.is_defined(compressor);
               case "||":
-                return this.left.is_truthy() || this.right.is_defined(compressor);
+                return node.left.is_truthy() || node.right.is_defined(compressor);
               case "??":
-                return this.left.is_defined(compressor) || this.right.is_defined(compressor);
+                return node.left.is_defined(compressor) || node.right.is_defined(compressor);
               default:
                 return true;
             }
+        }
+        def(AST_Assign, function(compressor) {
+            var op = this.operator;
+            if (op == "=") return this.right.is_defined(compressor);
+            return is_binary_defined(compressor, op.slice(0, -1), this);
+        });
+        def(AST_Binary, function(compressor) {
+            return is_binary_defined(compressor, this.operator, this);
         });
         def(AST_Conditional, function(compressor) {
             return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);


Test patch:
diff --git a/test/compress/assignments.js b/test/compress/assignments.js
index db562292485..80538bd8df3 100644
--- a/test/compress/assignments.js
+++ b/test/compress/assignments.js
@@ -589,3 +589,17 @@ issue_4815_2: {
     expect_stdout: "PASS"
     node_version: ">=15"
 }
+
+issue_4819: {
+    options = {
+        comparisons: true,
+    }
+    input: {
+        console.log(void 0 === ([].p &&= 42));
+    }
+    expect: {
+        console.log(void 0 === ([].p &&= 42));
+    }
+    expect_stdout: "true"
+    node_version: ">=15"
+}