Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    try {
        var foo_2 = --b + function a_2() {
            var bar_1 = delete b, b = a_2 = (c = c + 1) + {
                NaN: (c = 1 + c, (([ , 0 ].length === 2 ^ undefined) !== ({} & 23..toString())) << (-0 && 22) * (-4 || "number")),
                foo: (c = 1 + c, foo_2 && (foo_2[1 === 1 ? a : b] += ((2 ^ NaN) != (a_2 <<= false << -0)) * (a_2 = -0 === "c" || ~"b"))),
                "": (c = 1 + c, bar_1 && (bar_1[/[abc4]/g.exec(((c = c + 1) + ("undefined" in [ (c = 1 + c, 
                ("number" < "" | 0 + -4) >= (-3 > []) >>> (NaN || this)), (c = 1 + c, (c = c + 1, 
                "" - -0) * (c = c + 1, 5 && 0)), (c = 1 + c, [ , 0 ][1] + false > (-0 ^ "number") === (a_2 = (c = c + 1, 
                -2) & (-0 && "number"))), (c = 1 + c, ((c = c + 1, -4) >> ({} | 5)) / (5 - {} <= ("a" & 4))) ]) || b || 5).toString())] <<= ([ , 0 ][1] >> -5 != "undefined" % NaN) >= ((-0 && 23..toString()) ^ "object" >>> 4))),
                1.5: (c = 1 + c, (/[a2][^e]+$/ << 2 && -4 < "foo") < ([] - 0 || -2 >= 0))
            };
            {
                throw /[abc4]/g.exec(((c = c + 1) + -a || b || 5).toString());
            }
            try {
                {
                    var brake6 = 5;
                    do {
                        var a = typeof foo_2 !== "special", bar_1_2 = typeof b_1;
                    } while ({} && --brake6 > 0);
                }
            } catch (NaN_2) {
                var a = (c = c + 1) + [ (c = 1 + c, (foo_2 && (foo_2.c = -4 >= 2)) * (null || 1) >>> ((c = c + 1, 
                23..toString()) == ([ , 0 ].length === 2, 23..toString()))), (c = 1 + c, void (2 ^ null && [ , 0 ][1] != 0)), (c = 1 + c, 
                "b" === 25 !== "object" <= 2 ^ this >> false !== "undefined" % []) ];
                {
                    var c_1 = function f1(a_2) {
                    }();
                }
            }
            try {
                try {
                    {
                        var c_1_2 = function bar_2(foo, Infinity_2) {
                        }(4, (c = 1 + c, ((c_1 += 3 <= NaN) | -1 == 4) ^ "b" * "undefined" == (5 != undefined)));
                    }
                } finally {
                    {
                        var brake13 = 5;
                        while ((c = c + 1) + ((c = 1 + c, (c = c + 1, bar_1 && (bar_1[c = 1 + c, (-4 <= -1 | -0 == 25) !== 24..toString() >>> -4 >>> (bar_1_2 *= (-2, 
                        Infinity))] += (0, 24..toString()))) % -~"b") ? (c = 1 + c, ("b" + Infinity ^ -0 % {}) / (c = c + 1, 
                        "" + 3)) : (c = 1 + c, (0 > "undefined" ^ ("a" ^ -1)) < ("function", "bar") * (-4 / 38..toString()))) && --brake13 > 0) {
                            var brake14 = 5;
                            while ((c = 1 + c, ((c = c + 1, "number") && [] >> -1) + (38..toString() < Infinity ^ +this)) && --brake14 > 0) {
                                c = 1 + c, (a_2 = 38..toString() << "undefined") / (false < false) >> ("undefined" >> "bar" || "a" > -3);
                            }
                        }
                    }
                    try {
                        return;
                    } finally {
                        c = 1 + c, foo_2 && (foo_2[--b + [ (c = 1 + c, ("c" && 24..toString()) >>> ({} >>> 3) === (5 | "undefined") < -4 << ""), (c = 1 + c, 
                        (c = c + 1, Infinity != false) | (bar_1_2 && (bar_1_2.a += (null, -4))) << (5 & 25)), (c = 1 + c, 
                        a_2 && (a_2.a = "" < NaN != (c_1_2 && (c_1_2[c = 1 + c, ("undefined" && 3) << (bar_1_2 = -4 < this) ^ ([ , 0 ][1] % NaN || -0 >> undefined)] = true >> true)) && (bar_1_2 && (bar_1_2.a = [] >> -2)) ^ "a" === "undefined")), (c = 1 + c, 
                        (-5 % -4 || 2 ^ "") !== (c_1_2 &= (-2 | 38..toString()) / (/[a2][^e]+$/ & NaN))), (c = 1 + c, 
                        false >> -2 >>> ([ , 0 ].length === 2) * 1 === (c = c + 1, 3) * (foo_2 += 24..toString() >> true)) ][[ , 0 ][1] in {
                            NaN: (c = 1 + c, a_2 && (a_2.c += "c" === "" === true / -5 && ("number" >> "foo") + 24..toString() / 0)),
                            c: (c = 1 + c, (38..toString() ^ 1 | "undefined" < 23..toString()) >> ((-0 ^ 22) >= "c" * -1)),
                            "-2": (c = 1 + c, delete ~-2 > (c = c + 1, "b" >> null))
                        }]] += (4 >> 22 >> -4 / 22) % ((c = c + 1, "a") <= (false >= "b")));
                        c = 1 + c, c = c + 1, c_1 && (c_1[--b + (0 === 1 ? a : b)] += (c_1_2 && (c_1_2.in *= 3 * -1)) >= ("foo" ^ 0));
                    }
                }
            } catch (b) {
                {
                    return --b + ((5 != [ , 0 ][1]) + (1 == -1) !== (("c" || 23..toString()) != Infinity * this));
                }
                {
                    var expr21 = --b + [][a++ + (1 <= 24..toString() ^ 4 === /[a2][^e]+$/) * ((undefined == null) >> (a_2 && (a_2.var += (NaN, 
                    -2))))];
                    for (var key21 in expr21) {
                        {
                            var brake22 = 5;
                            while ((bar_1 = (c = 1 + c, (undefined ^ "") > (foo_2 && (foo_2.a += NaN / ([ , 0 ].length === 2))) > ("number" < -5) - (/[a2][^e]+$/ - 2))) && --brake22 > 0) {
                                try {
                                    c = 1 + c, +(([ , 0 ].length === 2) << "function") > (25 > null == 3 * 0);
                                } finally {
                                }
                            }
                        }
                    }
                }
            } finally {
                c = c + 1;
                switch ("b") {
                  case (c = c + 1) + (2 in [ (c = 1 + c, (false ^ -1) / (38..toString() != 0) != 0 + this < ("number" | -0)), (c = 1 + c, 
                    ((-4 || NaN) ^ (c_1_2 && (c_1_2.a = 4 === "undefined"))) >> ((0 ^ 5) & ("" | "number"))), (c = 1 + c, 
                    (foo_2 && (foo_2.b <<= (c = c + 1, -0) || -2 != "number")) + (("object" == -1) >>> (0 > "b"))), (c = 1 + c, 
                    (bar_1_2 += (1 && undefined) | (NaN && "bar")) >>> (Infinity << -5 >> ("b" >> 24..toString()))), (c = 1 + c, 
                    ("object" >>> 1 < (c_1_2 && (c_1_2[c = 1 + c, ("a" && -3) * ("undefined" + []) && Infinity % [] % (25 >>> "")] = "" ^ false))) / ((0 && "foo") <= /[a2][^e]+$/ % false)) ]):
                    c = c + 1;
                    {
                        var expr28 = (c = 1 + c, c = c + 1, "c" + "function" ^ "c" - 25);
                        for (var key28 in expr28) {
                            c = 1 + c;
                            var b_1 = expr28[key28];
                            c = 1 + c, (NaN >> 23..toString()) / ("c" ^ true) || "object" == null == -0 >= "";
                        }
                    }
                    break;

                  case (c = c + 1) + a--:
                    {
                        var expr30 = (c = 1 + c, ("object" !== 22) >>> (4 !== [ , 0 ][1]) ^ "function" + 25 === "object" >> [ , 0 ][1]);
                        for (c_1 in expr30) {
                            c = 1 + c, (1 * this && 2 != false) / ((c_1.undefined = "foo" << this) !== true > "c");
                        }
                    }
                    {
                        var expr32 = (c = 1 + c, ([] < -3 ^ "number" != "function") <= (foo_2 && (foo_2.Infinity = null <= -2 < "number" << -3)));
                        for (var key32 in expr32) {
                            c = 1 + c;
                            var parseInt_2 = expr32[key32];
                            c = 1 + c, "bar" & 38..toString() ^ "undefined" + "object" || -2 * -3 >> ("" != Infinity);
                        }
                    }
                    break;

                  case [ (c = 1 + c, ~-0 & (-4 | true) ^ ([] & this) >= -4 >> 5), (c = 1 + c, ((22 ^ -3) >>> "a" % []) / ((-4 + 2) % ("function" || this))) ][true > 24..toString() < ("bar" <= NaN) !== (c = c + 1, 
                    3) * ("undefined" != "b")]:
                    break;

                  default:
                }
            }
        }();
    } catch (bar_2) {
        {
            var brake34 = 5;
            while ((b += a) && --brake34 > 0) {
                const bar_2 = (c = c + 1) + /[abc4]/.test((!b || b || 5).toString());
            }
        }
        {
            return (c = c + 1) + void function undefined_2() {
                {
                    var expr37 = bar_2;
                    for (var key37 in expr37) {
                        c = 1 + c;
                        var arguments = expr37[key37];
                    }
                }
                return (c = c + 1) + a--;
                {
                    var brake40 = 5;
                    do {
                        a++ + {
                            in: (c = 1 + c, (-3 !== "a" || (c = c + 1, "b")) ^ (undefined_2 && (undefined_2.null += "c" <= "b")) >> (true != 5))
                        }[a++ + (typeof bar_2 == "function" && --_calls_ >= 0 && bar_2((c = 1 + c, undefined_2 && (undefined_2[(c = c + 1) + --b] = delete ("undefined" == -0) / ((3 || "undefined") | (-2 | "undefined")))), (c = 1 + c, 
                        (bar_2 = (NaN === []) < (NaN >= -2)) || (undefined_2 = null >> 22) == (23..toString(), 
                        0)), (c = 1 + c, 1 % -1 > (/[a2][^e]+$/ <= "function") != (undefined_2 && (undefined_2[[ (c = 1 + c, 
                        "function" % -0 / (undefined_2 && (undefined_2[c = 1 + c, (c = c + 1, "bar") / ([] + "number") + ((c = c + 1, 
                        3) | -5 === 23..toString())] = 22 / 5)) != (c = c + 1, "function") + (24..toString() << 23..toString())), , (c = 1 + c, 
                        undefined_2 && (undefined_2.Infinity = (-5 === 2) / (1 & 4) * (Infinity >> "a" != [ , 0 ][1] * -4))), (c = 1 + c, 
                        (-5 % -5 >= (this & 3)) / (-2 ^ -4 | "b" / "foo")), (c = 1 + c, ({} <= this) * (foo_2 && (foo_2.Infinity += ("bar", 
                        "a"))) / (null + "bar" <= (NaN < []))) ].null] *= (-5, 22, -5 !== []))))))];
                    } while (typeof undefined_2 == "function" && --_calls_ >= 0 && undefined_2(NaN) && --brake40 > 0);
                }
            }();
        }
    }
    var b_1 = ~b, bar = a++ + (b_1 && typeof b_1.length == "function" && --_calls_ >= 0 && b_1.length([ (c = c + 1) + ((0 & "c") >>> (b_1 = "b" == "a") != (-3 !== "a", 
    -4 == /[a2][^e]+$/)), {}[a++ + [ [ (c = 1 + c, -((NaN == /[a2][^e]+$/) < (false || 23..toString()))), (c = 1 + c, 
    ((-0, this) >= 0 / null) * (b_1 && (b_1[a++ + +b] = (25, -2) % (5 || 38..toString())))) ], /[abc4]/g.exec(((c = 1 + c, 
    "c" < /[a2][^e]+$/ != (NaN ^ "undefined") != (foo_2 += ("foo" > false) / (-5 < this))) || b || 5).toString()), 1 === 1 ? a : b, a++ + ("c" >>> "a" !== (false === this) | (c = c + 1, 
    Infinity | -1)), foo_2 && typeof foo_2.NaN == "function" && --_calls_ >= 0 && foo_2.NaN(38..toString(), 25) ].Infinity], (b_1 && b_1[(c = c + 1) + b_1] || 5).toString()[a++ + (38..toString() in {}) ? --b + (b_1 && b_1.foo) : typeof NaN_2], typeof Math_2 === "symbol" ], "undefined"));
}

var Math_2 = f0("b", -3, (c = c + 1) + (typeof b !== "special"));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    try {
        var foo_2 = --b + function a_2() {
            var a, bar_1 = delete b, b = (c += 1) + {
                NaN: (c = 1 + c, ((2 === [ , 0 ].length ^ void 0) != ({} & 23..toString())) << 0),
                foo: (c = 1 + c, foo_2 && (foo_2[a] += -1 * (2 != a_2 << 0))),
                "": (c = 1 + c, bar_1 && (bar_1[/[abc4]/g.exec(((c += 1) + ("undefined" in [ ([] < -3) >>> this <= -4, (c = 1 + (c = 1 + c), 
                c += 1, 0 * 0), (c = 1 + (c += 1), !1 === (-0 & -2)), (c = 1 + (c += 1), c += 1, 
                (-4 >> (5 | {})) / (5 - {} <= 0)) ]) || b || 5).toString())] <<= !0)),
                1.5: (c = 1 + c, 0 < (+[] || !1))
            };
            throw /[abc4]/g.exec(((c += 1) + -a || b || 5).toString());
        }();
    } catch (bar_2) {
        for (var brake34 = 5; (b += a) && 0 < --brake34; ) {
            var bar_2 = (c += 1) + /[abc4]/.test((!b || b || 5).toString());
        }
        return (c += 1) + void function() {
            var key37, expr37 = bar_2;
            for (key37 in expr37) {
                c = 1 + c;
                expr37[key37];
            }
            c += 1, a--;
        }();
    }
    var b_1 = ~b;
    a++, b_1 && "function" == typeof b_1.length && 0 <= --_calls_ && b_1.length([ (c += 1) + (0 >>> (b_1 = !1) != !1), {}[a++ + [ [ (c = 1 + c, 
    -(!1 < 23..toString())), (c = 1 + c, (NaN <= this) * (b_1 && (b_1[a++ + +b] = -2))) ], /[abc4]/g.exec((c = 1 + c, 
    (0 != (foo_2 += !1 / (-5 < this)) || b || 5).toString())), a, a++ + (0 !== (!1 === this) | (c += 1, 
    -1)), foo_2 && "function" == typeof foo_2.NaN && 0 <= --_calls_ && foo_2.NaN(38..toString(), 25) ].Infinity], (b_1 && b_1[(c += 1) + b_1] || 5).toString()[a++ + (38..toString() in {}) ? --b + (b_1 && b_1.foo) : typeof NaN_2], "symbol" == typeof Math_2 ], "undefined");
}

var Math_2 = f0("b", -3, (c += 1) + ("special" != typeof b));

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
null 99 509 16 Infinity NaN undefined

uglified result:
null 99 509 13 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
function f0() {
    try {
        (function a_2() {
            throw (0).toString();
        })();
    } catch (bar_2) {
        {
            const bar_2 = 0;
        }
        return function undefined_2() {
            var expr37 = bar_2;
            for (var key37 in expr37) {
                0();
            }
        }();
    }
}

f0();
// output: TypeError: 0 is not a function
// minify: 
// options: {
//   "mangle": false,
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  reduce_vars
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index fc65e426dcd..f930dd82f09 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -6640,7 +6640,13 @@ merge(Compressor.prototype, {
                 if (def.scope === scope) return true;
                 return !scope.variables.has(node.name) && !scope.globals.has(node.name);
             }
-            return def.scope === scope || !scope.find_variable(node);
+            if (def.scope === scope) return true;
+            var s = def.scope;
+            do {
+                s = s.parent_scope;
+                if (s.variables.has(node.name)) return false;
+            } while (s !== scope);
+            return true;
         }) ? make_node(AST_Var, self, {
             definitions: self.definitions.map(function(defn) {
                 var name = make_node(AST_SymbolVar, defn.name, defn.name);


Test patch:
diff --git a/test/compress/const.js b/test/compress/const.js
index f2b92247f61..72033f4a2d1 100644
--- a/test/compress/const.js
+++ b/test/compress/const.js
@@ -805,3 +805,38 @@ issue_4197: {
     }
     expect_stdout: "1"
 }
+
+issue_4198: {
+    options = {
+        reduce_vars: true,
+    }
+    input: {
+        console.log(function() {
+            try {
+                throw "PASS";
+            } catch (e) {
+                {
+                    const e = "FAIL";
+                }
+                return function() {
+                    return e;
+                }();
+            }
+        }());
+    }
+    expect: {
+        console.log(function() {
+            try {
+                throw "PASS";
+            } catch (e) {
+                {
+                    const e = "FAIL";
+                }
+                return function() {
+                    return e;
+                }();
+            }
+        }());
+    }
+    expect_stdout: "PASS"
+}