Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

{
    if ((c = c + 1) + /[abc4]/g.exec(((--b + a-- || a || 3).toString() || b || 5).toString())) {
        ((0 === 1 ? a : b) || a || 3).toString();
    }
}

{
    var bar_1 = function f0(arguments_2, bar_1, await) {
        function f1(arguments, b_1) {
            {
                return b--;
            }
            {
                var brake6 = 5;
                while (--b + (arguments_2 %= --b + (arguments_2 && arguments_2.c)) && --brake6 > 0) {
                    a++ + (22 in [ (c = 1 + c, -4, [ 3n ][0] > 2, 22 / 2, "b" >= "c" === /[a2][^e]+$/ / ([ , 0 ].length === 2)), (c = 1 + c, 
                    c = c + 1, Number(0xdeadn << 16n | 0xbeefn) << "bar" || await && (await[c = 1 + c, 
                    (!null & -2 >= null) <= (2 & "") * ("a" && 23..toString())] &= "b" + undefined)) ]);
                }
            }
        }
        var b_1 = f1`${--b + a--}${--b + (bar_1 = (c = c + 1) + b++)}`;
        function f2() {
            function f3(b_1_2, undefined_1, b_1) {
                c = c + 1;
                c = c + 1;
            }
            var b_1_2 = f3(((c = 1 + c, (("object" ^ Number(0xdeadn << 16n | 0xbeefn)) + (-2 !== "")) / ("object" === "undefined", 
            Infinity >>> Number(0xdeadn << 16n | 0xbeefn))) || 3).toString()[c = 1 + c, c = c + 1, 
            (arguments_2 = false ^ [ , 0 ].length === 2) != (-4 & "bar")], this);
            function f4(b_1_2_1, b_1) {
                {
                }
                if (c = 1 + c, "undefined" >= Number(0xdeadn << 16n | 0xbeefn) ^ (b_1_2 && (b_1_2.null = 4 / !0o644n)) || !"function" | (-5, 
                24..toString())) {
                    c = 1 + c, ("b" >>> 3, null > -3) % ((c = c + 1, -0) > (23..toString() && "bar"));
                }
            }
            var bar_1 = f4(arguments_2 && typeof arguments_2.length == "function" && --_calls_ >= 0 && arguments_2.length() ? a++ + typeof (c = 1 + c, 
            (-3 | "") - ("undefined" >= 5) | (b_1_2 && (b_1_2[!function() {
            }()] += (b_1_2 = [ 3n ][0] > 2 == 4) - (4 >= 24..toString())))) : {
                "-2": (c = 1 + c, 1 - 23..toString() << ~-2 | undefined << "c" === (22, "b")),
                0: (c = 1 + c, (38..toString() > /[a2][^e]+$/ !== (this === ([ , 0 ].length === 2))) / (-5 >> true == (/[a2][^e]+$/ ^ {}))),
                "\t": (c = 1 + c, (c = c + 1, "a" >> Infinity) ^ ("c" ^ "") >>> -3 * 38..toString())
            }, typeof b_1_2 == "function" && --_calls_ >= 0 && b_1_2(3));
            function f5(bar_1_2) {
                {
                    var brake13 = 5;
                    L28053: while ((c = 1 + c, -2 << -1 * Infinity >>> (([] === [ , 0 ][1]) > delete undefined)) && --brake13 > 0) {
                        c = 1 + c, c = c + 1, (bar_1 >>>= [ , 0 ].length === 2 ^ null) && -2 == 22;
                    }
                }
                var Infinity_1 = (c = 1 + c, (-42n).toString() >>> null >> "" / "number" == (24..toString() === -1) - (4 >= {}));
            }
            var b_1_1 = f5();
        }
        var b_1 = f2(Infinity);
        function f6(b_1, await_2, await_2_2) {
            {
                var brake16 = 5;
                L28054: do {
                    {
                        try {
                            c = 1 + c, (false && 5) >= (Infinity && 0), (b_1 && ([ b_1[c = 1 + c, c = c + 1, 
                            (!0o644n && []) !== (2 ^ [ , 0 ][1])] ] = [ (Infinity, "") ])) % (23..toString() !== -5);
                        } catch (await_1) {
                        } finally {
                        }
                    }
                } while (a++ + (-3 in {
                    c: b_1 && typeof b_1.foo == "function" && --_calls_ >= 0 && b_1.foo((-42n).toString()),
                    [(c = c + 1) + (b += a)]: a++ + a++,
                    foo: new function a_1(bar) {
                        this[c = 1 + c, await_2 && (await_2[delete ((true | null | ("object" | {})) != ([] >> [ , 0 ][1] || -0 % -3))] *= (5 % Number(0xdeadn << 16n | 0xbeefn) > this * 0) % ((bar_1 && (bar_1[c = 1 + c, 
                        (3 / 22 && 1 == -5) << (([ , 0 ].length === 2 || true) == 5 > 0)] += -5 << 3)) == "c" < "foo"))] = "object" >>> "number";
                        this.NaN = -0 + [];
                    }(),
                    0: typeof f0 == "function" && --_calls_ >= 0 && f0("bar", false, (c = 1 + c, (b_1 && (b_1[a++ + (typeof c === "object")] = (null < 25) >> -5 / !0o644n)) + (23..toString() << "c" >= ("bar" == [])))),
                    a: typeof (c = 1 + c, (-0 >>> 1 & -5 <= /[a2][^e]+$/) >= (c = c + 1, "" <= 2))
                }) && --brake16 > 0);
            }
            {
                return b_1 && b_1[await_2 && typeof await_2.length == "function" && --_calls_ >= 0 && await_2.length()];
            }
        }
        var foo_2 = f6((c = c + 1) + {}.Infinity, a++ + (b_1 && typeof b_1.Infinity == "function" && --_calls_ >= 0 && b_1.Infinity(a++ + typeof ((c = c + 1) + !function() {
        }()))));
        function f7(NaN, await_1, b_1) {
            function f8(foo_2) {
                {
                }
                {
                    return;
                    c = 1 + c, (arguments_2 = -0 > 4) * (25 != {}) - (null < this) * (-5 << "object");
                }
            }
            var c_2 = f8([ (c = 1 + c, (c_2 &= (4 ^ 5) == 4 <= -1) || (await_1 && (await_1.foo += "c" | {})) <= null << "a"), ..."" + arguments_2 ][c = 1 + c, 
            (5 < -3) << (/[a2][^e]+$/ !== 3) || 38..toString() % NaN - (Infinity - 23..toString())], "", --b);
            function f9(NaN_2, bar_2) {
                function f10([]) {
                }
                var await_1 = f10("", 24..toString());
                function f11(bar_2_1, foo_2_1) {
                }
                var c_2_2 = f11("", (c = 1 + c, (5 < "c") * ("c" << "undefined") <= ("number" / 1 != 25 > /[a2][^e]+$/)));
                function f12() {
                }
                var b_1 = f12("bar");
                function f13() {
                }
                var await_2 = f13("a");
            }
            var b_2 = f9();
        }
        var c_2 = f7();
    }``;
}

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var i = 10, e = 100, c = 10, u = 0;

(function f0(f, t, n) {
    var o = function f1(arguments, n) {
        return c--;
    }`${--c + e--}${--c + (t = (u += 1) + c++)}`, o = function f2() {
        var n = function f3() {
            u += 1, u += 1;
        }((u = 1 + (1 + u), u += 1, f = !1 ^ 2 === [ , 0 ].length));
        (function f4() {
            u = 1 + u, n && (n.null = 4 / !0o644n), u = 1 + u, u += 1;
        })(f && "function" == typeof f.length && 0 <= --i && f.length() ? (e++, u = 1 + u, 
        n && (n[!0] += (n = 2 < 3n == 4) - !1)) : (u = 1 + (1 + (1 + u)), u += 1), "function" == typeof n && 0 <= --i && n(3)), 
        function f5() {
            for (var n = 5; u = 1 + u, -2 >>> (delete undefined < !1) && 0 < --n; ) {
                u = 1 + u, u += 1;
            }
            u = 1 + u;
        }();
    }();
    (function f6(n, f) {
        var o = 5;
        do {
            try {
                u = 1 + u, n && ([ n[u = 1 + u, u += 1, 2 !== (!0o644n && [])] ] = [ "" ]);
            } catch (n) {}
        } while (e++ + (-3 in {
            c: n && "function" == typeof n.foo && 0 <= --i && n.foo("" + -42n),
            [(u += 1) + (c += e)]: e++ + e++,
            foo: new function a_1(n) {
                this[u = 1 + u, f && (f[!0] *= (0 * this < 5 % +("" + (0xdeadn << 16n | 0xbeefn))) % (1 == (t && (t[u = 1 + u, 
                !1 << (1 == (2 === [ , 0 ].length || !0))] += -40))))] = 0, this.NaN = "0";
            }(),
            0: 0 <= --i && f0("bar", !1, (u = 1 + u, n && (n[e++ + ("object" == typeof u)] = !0 >> -5 / !0o644n))),
            a: (u = 1 + u, u += 1, typeof !1)
        }) && 0 < --o);
        n && n[f && "function" == typeof f.length && 0 <= --i && f.length()];
    })((u += 1) + {}.Infinity, e++ + (o && "function" == typeof o.Infinity && 0 <= --i && o.Infinity(e++ + typeof ((u += 1) + !0)))), 
    function f7(n, f) {
        u = 1 + u, f && (f.foo += 0), u = 1 + u, --c, function f9() {
            ([ [] ] = [ "" ]), u = 1 + u;
        }();
    }();
})``, console.log(null, --e, --c, u += 1, Infinity, NaN, void 0);
```
```
original result:
null 264 9931 627 Infinity NaN undefined

uglified result:
null 264 9986 627 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var a = 0;

a--;

(function f0() {
    ({
        [console.log(a)]: 0
    });
    a++;
})``;

console.log(a);
// output: -1
// 0
//
// minify: 0
// 0
//
// options: {
//   "compress": {
//     "hoist_vars": true,
//     "keep_infinity": true,
//     "passes": 1000000,
//     "unsafe": true
//   },
//   "keep_fnames": true,
//   "toplevel": true,
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "compress": {
    "hoist_vars": true,
    "keep_infinity": true,
    "passes": 1000000,
    "unsafe": true
  },
  "keep_fnames": true,
  "toplevel": true,
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  collapse_vars
  conditionals
  pure_getters
  side_effects
  unsafe
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index f894a6cb2eb..ac7aa3e0b69 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -1942,6 +1942,7 @@ merge(Compressor.prototype, {
                     var def = node.definition();
                     return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
                 }
+                if (node instanceof AST_Template) return node.tag && !is_raw_tag(compressor, node.tag);
                 if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
                 if (node instanceof AST_VarDef) {
                     if (check_destructured(node.name)) return true;


Test patch:
diff --git a/test/compress/templates.js b/test/compress/templates.js
index ef2a534b9ef..3b0e983c620 100644
--- a/test/compress/templates.js
+++ b/test/compress/templates.js
@@ -198,3 +198,27 @@ unsafe_side_effects: {
     expect_stdout: "foo"
     node_version: ">=4"
 }
+
+issue_4604: {
+    options = {
+        collapse_vars: true,
+    }
+    input: {
+        var a = 0, log = console.log;
+        a = "FAIL";
+        (function() {
+            a = "PASS";
+        })``;
+        log(a);
+    }
+    expect: {
+        var a = 0, log = console.log;
+        a = "FAIL";
+        (function() {
+            a = "PASS";
+        })``;
+        log(a);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}