Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(foo_2, arguments_2, c_1) {
    function f1(Math_1, foo_2, a) {
        {
            var brake1 = 5;
            while ((c = c + 1) + (1 === 1 ? a : b) && --brake1 > 0) {
                --b + (b + 1 - .1 - .1 - .1);
            }
        }
        var bar_1 = 25;
    }
    var Math_1 = f1(2, "foo");
    function f2(arguments_2_2, NaN_1) {
        {
            var brake4 = 5;
            L19518: while (void (Infinity == {} ^ "bar" * "function" ^ "c" * "undefined" & (NaN_1 && (NaN_1.foo /= "undefined" & -4))) && --brake4 > 0) {
                var c_2 = function f3(bar, Math_1, b_2) {
                    c = 1 + c, (foo_2 && (foo_2.a += -1 || 25)) << ("bar" == -4) >= (b_2 && (b_2[--b + "a"] += (c = c + 1, 
                    [ , 0 ][1]) ^ -1 !== 25));
                    c = 1 + c, (("bar" >>> null) - (38..toString() == 4)) * (!-5 >> (Math_1 && (Math_1[c = 1 + c, 
                    ("foo" % 23..toString() ^ ([ , 0 ].length === 2 | NaN)) * ((22 || -4) / ("function" / 4))] = true || -5)));
                }((c = 1 + c, (Math_1 && (Math_1[/[abc4]/.test(((c = 1 + c, ([ , 0 ][1] > "b") << (-2 ^ null) !== (NaN & 2) < (NaN === "foo")) || b || 5).toString())] = "foo" >>> 3 === ([ , 0 ].length === 2, 
                3))) == -3 - [] <= (0 > "bar")));
            }
        }
        return;
    }
    var c_1 = f2();
    function f4(Infinity_2) {
        try {
            var NaN = typeof f2 == "function" && --_calls_ >= 0 && f2((c = 1 + c, null > 0 >= ("object" ^ -3) != ("undefined" || -0) - (4 ^ "object")), 22, -5), parseInt = typeof (typeof a_1 == "undefined");
        } catch (b_1) {
            1 === 1 ? a : b;
            (c = 1 + c, ("" === -4 ^ 24..toString() + 1) >= ("undefined" !== "undefined" ^ (/[a2][^e]+$/ || false))) ? (c = 1 + c, 
            "" % "object" >> (true >>> []) > (arguments_2 && (arguments_2.undefined >>= "undefined" + {} >> (Infinity >>> -3)))) : (c = 1 + c, 
            ("" && 0) != ("b" && [ , 0 ][1]) != (c_1 = (25, 5) <= (undefined === "a")));
        } finally {
            {
                var brake13 = 5;
                while (--b + [ (c = 1 + c, (25 * undefined ^ (/[a2][^e]+$/, /[a2][^e]+$/)) & ((Math_1 && (Math_1[c = 1 + c, 
                ("b" ^ /[a2][^e]+$/) - ([ , 0 ].length === 2 ^ "function") <= (({} & -3) > (25 < this))] = 24..toString() >>> -2)) & (foo_2 && (foo_2[c = 1 + c, 
                ("number" && "foo") <= ("c" | -1) > (23..toString() > "undefined" === (arguments_2 && (arguments_2[c = 1 + c, 
                (c = c + 1, "number") >>> (1 >> ([ , 0 ].length === 2)) <= (false !== 23..toString() ^ 25 == {})] = "b" * -4)))] = 2 >= null)))) ] && --brake13 > 0) {
                    c = 1 + c, ~("" / {}) || "undefined" <= 0 | null - "function";
                }
            }
            {
                return --b + (b = a);
            }
        }
    }
    var b = f4(function() {
        if (Math_1) {
            c = c + 1;
        }
    }());
}

var a_2 = f0(3, a++ + -(a_2 %= (([ , 0 ].length === 2) > "foo" ^ (c = c + 1, 22)) >> ((a_2 = Infinity / Infinity) << (false < /[a2][^e]+$/))));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var u = 10, n = 100, d = 0;

(function(t, f) {
    var e = function(n) {
        for (var i = 5; (d += 1) + n && 0 < --i; ) {
            --r;
        }
    }();
    function o(n, i) {
        Infinity, i && (i.foo /= 0);
    }
    o();
    var r = function() {
        try {
            0 <= --u && o(d = 1 + d, 22);
        } catch (i) {
            d = 1 + d, 0 <= (!1 ^ 24..toString() + 1) ? (d = 1 + d, f && (f.undefined >>= "undefined" + {} >> (Infinity >>> -3))) : (d = 1 + d, 
            undefined);
        } finally {
            for (var n = 5; --r + [ (d = 1 + d, (25 * undefined ^ /[a2][^e]+$/) & ((e && (e[d = 1 + d, 
            0 - (2 === [ , 0 ].length ^ "function") <= (25 < this < (-3 & {}))] = 24..toString() >>> -2)) & (t && (t[d = 1 + d, 
            ("undefined" < 23..toString() === (f && (f[d = 1 + d, d += 1, "number" >>> (1 >> (2 === [ , 0 ].length)) <= (!1 !== 23..toString() ^ 25 == {})] = NaN))) < !1] = !0)))) ] && 0 < --n; ) {
                d = 1 + d;
            }
            return --r + (r = 101);
        }
    }(e && (d += 1));
})(3, n++ - (n %= ("foo" < (2 === [ , 0 ].length) ^ (d += 1, 22)) >> (Infinity / Infinity << !1))), 
n = 0;

console.log(null, 101, 10, d, Infinity, NaN, undefined);
```
```
original result:
null 101 10 17 Infinity NaN undefined

uglified result:
null 101 10 27 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var a = 0;

function f0(foo_2, arguments_2) {
    console.log(arguments_2);
}

var a_2 = f0(3, a++ + (a_2 %= 1 >> console.log(a_2 = 0)));

a;
// output: 0
// NaN
// 
// minify: 0
// 0
// 
// options: {
//   "ie8": true,
//   "toplevel": true,
//   "validate": true
// }
```
```
minify(options):
{
  "ie8": true,
  "toplevel": true
}

Suspicious compress options:
  evaluate
  merge_vars
  reduce_vars
  unused

Suspicious options:
  toplevel
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index c39c0da60a9..3c14a61a027 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4315,10 +4315,11 @@ merge(Compressor.prototype, {
         var prev = Object.create(null);
         var tw = new TreeWalker(function(node, descend) {
             if (node instanceof AST_Assign) {
+                if (node.operator != "=") return;
                 var sym = node.left;
                 if (!(sym instanceof AST_SymbolRef)) return;
                 node.right.walk(tw);
-                mark(sym, node.operator == "=");
+                mark(sym, true);
                 return true;
             }
             if (node instanceof AST_Binary) {


Test patch:
diff --git a/test/compress/merge_vars.js b/test/compress/merge_vars.js
index 8ef186cf34f..e4494849ec5 100644
--- a/test/compress/merge_vars.js
+++ b/test/compress/merge_vars.js
@@ -231,3 +231,30 @@ read_before_assign_2: {
     }
     expect_stdout: "PASS"
 }
+
+issue_4103: {
+    options = {
+        merge_vars: true,
+        side_effects: true,
+        toplevel: true,
+    }
+    input: {
+        function f(a) {
+            console.log(a);
+        }
+        var b = 0;
+        var c = f(b++ + (c %= 1 >> console.log(c = 0)));
+        b;
+    }
+    expect: {
+        function f(a) {
+            console.log(a);
+        }
+        var b = 0;
+        var c = f(b++ + (c %= 1 >> console.log(c = 0)));
+    }
+    expect_stdout: [
+        "0",
+        "NaN",
+    ]
+}