Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

{
    var b = function f0() {
        const bar_2 = (c = c + 1) + (typeof f2 == "function" && --_calls_ >= 0 && f2("function", [ , 0 ].length === 2, 2));
        function f1(a) {
            for (var brake2 = 5; (c = c + 1) + [ void b, --b + (typeof b !== "undefined"), ,  ][--b + (bar_2 && bar_2.var)] && brake2 > 0; --brake2) {
                switch (function foo_2() {
                    c = 1 + c, bar_2 && (bar_2[--b + (1 === 1 ? a : b)] += (a ^= "object" * 1 || "object" & "number") < (true + 24..toString()) / (5 != 2));
                }()) {
                  case --b + /[abc4]/.test(((c = 1 + c, (bar_2 && (bar_2[--b + (bar_2 && typeof bar_2.a == "function" && --_calls_ >= 0 && bar_2.a((c = 1 + c, 
                    bar_2 && (bar_2.null += "a" >= -4 === 25 >>> "c" != ((1 & null) == "object" / -0))), 24..toString(), "function"))] >>= ("object" && 23..toString()) - (-1 >> 5))) * ({} % 38..toString() >>> ("b", 
                    23..toString()))) || b || 5).toString()):
                    try {
                        c = 1 + c, (NaN !== -2 == delete Infinity) > ("a" >> null) - ("object" || 3);
                    } catch (NaN) {
                    }
                    ;
                    break;

                  case (c = c + 1) + (0 === 1 ? a : b):
                    break;

                  default:
                    switch (c = 1 + c, ~(bar_2 && (bar_2[typeof f1 == "function" && --_calls_ >= 0 && f1((c = 1 + c, 
                    (23..toString() + [ , 0 ][1] & "bar" === 1) * (-"" + (5 >= "foo"))), (c = 1 + c, 
                    delete (-4 > 23..toString()) * (bar_2 && (bar_2.foo = (2 << NaN, "bar" <= ([ , 0 ].length === 2))))), 24..toString())] *= 4 >> "undefined" < (bar_2 && (bar_2.var = {} < 5))))) {
                      case c = 1 + c, (38..toString() <= this || -5 !== NaN) !== (4 === "foo" == {} % 5):
                        ;
                        break;

                      case c = 1 + c, "a" % null > (Infinity == "") == (-3 < 5) - (3 << "number"):
                        ;
                        break;

                      case c = 1 + c, a += 24..toString() === "object" == "" > 5 || ("bar" || "function") | {} & 4:
                        ;
                        break;

                      case c = 1 + c, (-5 % -1 - (c = c + 1, "number")) * ("bar" + 2 === "c" < this):
                        ;
                        break;
                    }

                  case {
                        0: (c = 1 + c, (24..toString() && "foo") >>> (24..toString() || 22) == (a *= "" <= /[a2][^e]+$/, 
                        "undefined" - -3)),
                        var: (c = 1 + c, ("number" !== "bar") + (c = c + 1, {}) >= ([] ^ undefined) + ("a" + /[a2][^e]+$/)),
                        b: (c = 1 + c, 23..toString() * "b" - (null ^ {}), "b" + "number" << ([] >= {})),
                        a: (c = 1 + c, (a = 2 & -5) / ([ , 0 ].length === 2 === "") | (23..toString() == undefined | 22 == 25))
                    }[{
                        "": (c = 1 + c, ("" | "undefined" | "object" < 24..toString()) + (("number" || -0) >> (24..toString() && 22))),
                        3: (c = 1 + c, (Infinity | [ , 0 ][1]) !== (a *= -3 !== 38..toString()) !== ("undefined" ^ 2) - ("number" - null))
                    }]:
                    {
                        var expr9 = (c = 1 + c, -5 - "b" ^ (1, "") || "undefined" | -2 && this == false);
                        for (var key9 in expr9) {
                            c = 1 + c, ("number" < -3 & 22 / NaN) + ((4 & 23..toString()) == 25 < false);
                        }
                    }
                    break;
                }
            }
            c = c + 1;
        }
        function f2() {
            {
                var brake12 = 5;
                do {
                    typeof bar_2;
                } while (a++ && --brake12 > 0);
            }
            {
                var expr14 = (c = c + 1) + (b += a);
                for (var key14 in expr14) {
                    c = 1 + c;
                    var foo = expr14[key14];
                    {
                        var Infinity = function b_1(parseInt_1, b, a_2) {
                            c = 1 + c, ((bar_2 && (bar_2[c = 1 + c, "function" >= /[a2][^e]+$/ ^ 2 + null ^ 5 === null === "c" << "number"] = 3 != "a")) !== "undefined" < "object") + ((-4 !== -2) > "number" * Infinity);
                            c = 1 + c, c = c + 1, "function" < [ , 0 ][1] & (-1 ^ -1);
                        }();
                    }
                }
            }
        }
        function f3(a, a, NaN_1) {
            var undefined_2;
        }
        var a = f3((c = c + 1) + b++);
        function f4(b) {
            function f5() {
                {
                    var brake20 = 5;
                    while ((c = 1 + c, ("number" ^ 1) * (3 ^ 2) ^ (c = c + 1, 23..toString()) !== true >= -3) && --brake20 > 0) {
                        c = 1 + c, undefined << -1 == 0 - -5 & (1 < /[a2][^e]+$/ && 24..toString() & /[a2][^e]+$/);
                    }
                }
                return c = 1 + c, (bar_2 && (bar_2.NaN = (1 >>> {}) / ("foo" <= 1))) | (c = c + 1, 
                false + "undefined");
            }
            var b_2 = f5(22, --b + ((b_2 && (b_2.a = this > -0)) + (bar_2 && (bar_2[c = 1 + c, 
            (b_2 && (b_2.null >>>= ("a", [ , 0 ].length === 2) || true << /[a2][^e]+$/)) + (bar_2 && (bar_2.foo %= (null, 
            "foo") ^ 4 == {}))] *= "object" * 2)) + (this < "" <= (0 === "object"))), a++ + (typeof bar_2 == "function" && --_calls_ >= 0 && bar_2(true, "c")));
            function f6(b) {
                {
                    return c = 1 + c, 4 + 23..toString() > ("object" || NaN) ^ +(this > ([ , 0 ].length === 2));
                }
                {
                    var brake24 = 5;
                    while ((c = 1 + c, c = c + 1, (1 ^ 2) <= 3 - 24..toString()) && --brake24 > 0) {
                        c = 1 + c, (b_2 && (b_2.NaN = 1 & {} && 1 & {})) & ~"object" == (c = c + 1, 38..toString());
                    }
                }
            }
            var NaN = f6((c = c + 1) + (b + 1 - .1 - .1 - .1), 22);
            function f7(foo_2, bar_2_2, undefined) {
                switch (c = 1 + c, ("function" === 5 | 2 != [ , 0 ][1]) & ("c" ^ 23..toString()) + ("number" > 25)) {
                  case c = 1 + c, (-0 / 25 | 3 << NaN) === ("number" || "a") >>> (3 || "a"):
                    ;
                    break;

                  case c = 1 + c, (("object" !== null) - (-3 > 4)) / (3 != null !== 24..toString() >= "function"):
                    ;
                    break;

                  case c = 1 + c, c = c + 1, ("function" ^ -1) - (23..toString() < 24..toString()):
                    ;
                    break;

                  case c = 1 + c, ((c = c + 1, 38..toString()) || 2 + true) < (c = c + 1, -4) >> ("function" >> true):
                    ;
                    break;
                }
            }
            var a_1 = f7();
            function f8(b_2_2) {
                {
                    var expr28 = (c = 1 + c, (-2 < "number") % (38..toString() || 25) || [] + "a" >>> ("undefined" || -2));
                    for (var key28 in expr28) {
                        c = 1 + c, (c = c + 1, a_1 && (a_1[c = 1 + c, b_2_2 && (b_2_2[24..toString() in [ (c = 1 + c, 
                        (-5 ^ this) + ~3 >>> ((24..toString() ^ 0) <= (0 <= -0))), (c = 1 + c, (a_1 &= "" + "undefined") - ([ , 0 ][1] && [ , 0 ].length === 2) < (-2 % 25 ^ (Infinity && 25))), (c = 1 + c, 
                        bar_2 && (bar_2[{}[--b + (typeof f2 == "function" && --_calls_ >= 0 && f2((c = 1 + c, 
                        (1 & 2) + (5 != "bar") & ("b" != 5) <= (-5 && 24..toString())), 23..toString(), (c = 1 + c, 
                        (c = c + 1, b_2 && (b_2.var *= null - Infinity)) ^ "bar" >> "b" == (null == Infinity))))]] = (-4 >> "c" ^ 5 > false) >> (("b" && -0) > (25 < null)))) ]] ^= (b_2_2 && (b_2_2["function" in [ (c = 1 + c, 
                        b_2 && (b_2[--b + (b_2_2 && typeof b_2_2.in == "function" && --_calls_ >= 0 && b_2_2.in(-0, /[a2][^e]+$/))] += (23..toString() % /[a2][^e]+$/ !== "" >>> "function", 
                        undefined > 22 & -1 >> 3))), (c = 1 + c, c = c + 1, ("b" ^ -1) & -4 < 2) ]] = "" % "foo" * (-2 !== 38..toString()))) > ("number" | "") + ("a" != ""))] += -4 >= Infinity)) != (b_2_2 = "foo" / undefined) >> -3 % "";
                    }
                }
            }
            var NaN = f8(null);
            function f9(Infinity, foo_2, a) {
                function f10(a, Math_2) {
                }
                var a = f10("object");
                function f11(parseInt, a_2, a) {
                }
                var b = f11(-1, (c = 1 + c, a = 24..toString() - 38..toString() + ("number" !== undefined) << ("object" ^ 3) / (24..toString() <= 24..toString())));
                function f12(b_2_2, foo_2_2) {
                }
                var b_2 = f12((c = 1 + c, ~(-1 > -3) <= (foo_2 = /[a2][^e]+$/ % 38..toString() > ([ , 0 ][1] !== false))), (c = 1 + c, 
                22 % [] - ([] >> null) || (bar_2 && (bar_2[c = 1 + c, (-4 ^ this ^ undefined > "number") <= -this - (-0 - "c")] /= 22 << "object")) <= (2 <= [])), "b");
                function f13(b_2, bar_1, b_2) {
                }
                var foo_2 = f13();
            }
            var bar_1 = f9();
        }
        var b_1 = f4(a++ + /[abc4]/g.exec(((c = c + 1) + (typeof b ? a++ + ([] in [ (c = 1 + c, 
        ((true & -0) < (23..toString() == /[a2][^e]+$/)) >>> ("c" << 38..toString() <= (22 != 3))), (c = 1 + c, 
        "b" >= -5 > 38..toString() >>> "foo" || 22 % -1 | "b" < "") ]) : NaN % /[a2][^e]+$/ + (Infinity << 22) ^ -1 - -4 - (NaN <= 24..toString())) || b || 5).toString()), 0);
        function f14(parseInt_1, foo_2, foo_2) {
            {
                var brake31 = 5;
                while (--b + (0 === 1 ? a : b) && --brake31 > 0) {
                    var expr32 = --b + typeof (--b + void b);
                    L16457: for (var key32 in expr32) {
                        c = c + 1;
                    }
                }
            }
            if (typeof f15 == "function" && --_calls_ >= 0 && f15()) {
                try {
                    foo_2 && foo_2.NaN;
                } finally {
                    {
                    }
                    c = c + 1;
                }
            } else {}
        }
        var NaN_2 = f14(-0);
    }((+(a && (a.in = (-3 >= 23..toString()) / ([ , 0 ][1] >= -3) | "bar" != "c" === (24..toString() & -0))) || a || 3).toString(), a++ + (a++ + ((1 & "a") >> (true >>> 23..toString()) ^ (a = 25 <= "object" != (a && (a.undefined = [ , 0 ][1] !== []))))), a = (c = c + 1) + [ (c = c + 1) + (typeof a == "function" && --_calls_ >= 0 && a(24..toString())), a++ + (b ^= a), typeof f16 == "function" && --_calls_ >= 0 && f16({
        "-2": --b + typeof bar_1
    }, -2 in {
        1.5: typeof a == "function" && --_calls_ >= 0 && a(23..toString()),
        0: (c = c + 1) + [ (c = 1 + c, (-0 % 25 < (a && (a.b *= ("c", -5)))) / (-1 > -2 ^ ("foo" && Infinity))), (c = 1 + c, 
        (c = c + 1, -2 < NaN) ^ "" >= -4 >= false - 0), (c = 1 + c, (a && (a.Infinity = (true, 
        4) % (this | -0))) | (a && (a[(c = c + 1) + -delete (38..toString() >>> false ^ (3 || "function"))] = ({} == "") * (a && (a.var += "number" <= 38..toString()))))) ][c = 1 + c, 
        a && (a[--b + void function() {}()] += (25 >> 22) - (this >> /[a2][^e]+$/) === (({} & undefined) == ("number" == "undefined")))],
        Infinity: (c = c + 1) + ++a
    }), (c = c + 1) + typeof (a++ + (typeof a == "function" && --_calls_ >= 0 && a("", 24..toString(), --a))) ]);
}

var bar = a && (a.c = -3 * undefined !== true > ([ , 0 ].length === 2) ^ (a && (a[c = 1 + c, 
(-4 >> 2, "bar" * 0) % ("b" << "object" !== (a && (a[c = 1 + c, (24..toString() * "a" ^ 23..toString() != ([ , 0 ].length === 2)) >= ("c" <= Infinity, 
0 + "c")] %= -2 / /[a2][^e]+$/)))] = "c" && 5) && this >>> ""));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0, b = function() {
    const bar_2 = (c += 1) + (0 <= --_calls_ && f2());
    function f2() {
        for (var brake12 = 5; a++ && 0 < --brake12; ) {}
        var key14, expr14 = (c += 1) + (b += a);
        for (key14 in expr14) {
            c = 1 + (c = 1 + c), bar_2 && (bar_2[c = 1 + c, 3] = !0), c = 1 + c, c += 1;
        }
    }
    var a = (c += 1, void b++);
    !function(b) {
        var b_2 = function() {
            for (var brake20 = 5; c = 1 + c, 1 ^ !0 !== (c += 1, 23..toString()) && 0 < --brake20; ) {
                c = 1 + c;
            }
            return c = 1 + c, (bar_2 && (bar_2.NaN = (1 >>> {}) / !1)) | (c += 1, !1 + "undefined");
        }((--b, b_2 && (b_2.a = -0 < this), bar_2 && (bar_2[c = 1 + c, (b_2 && (b_2.null >>>= 2 === [ , 0 ].length || 1)) + (bar_2 && (bar_2.foo %= "foo" ^ 4 == {}))] *= 0 / 0), 
        a++, "function" == typeof bar_2 && 0 <= --_calls_ && bar_2(!0, "c"))), NaN = function() {
            return c = 1 + c, "object" < 4 + 23..toString() ^ +((2 === [ , 0 ].length) < this);
        }(c += 1), a_1 = function() {
            switch (c = 1 + c, 1 & ("c" ^ 23..toString()) + !1) {
              case c = 1 + c, 0 == (-0 | 3 << NaN):
              case c = 1 + c, 1 / (!0 != "function" <= 24..toString()):
              case c = 1 + c, c += 1, -1 - (23..toString() < 24..toString()):
              case c = 1 + c, c += 1, (38..toString() || 3) < (c += 1, -4):
            }
        }();
        NaN = function(b_2_2) {
            for (var key28 in c = 1 + c, !1 % (38..toString() || 25) || [] + "a" >>> "undefined") {
                c = 1 + c, c += 1, a_1 && (a_1[c = 1 + c, b_2_2 && (b_2_2[24..toString() in [ (c = 1 + c, 
                (-5 ^ this) - 4 >>> ((0 ^ 24..toString()) <= !0)), (a_1 &= "undefined") < -25, (c = 1 + (c = 1 + c), 
                bar_2 && (bar_2[{}[--b + (0 <= --_calls_ && f2((c = 1 + c, 24..toString()), 23..toString(), (c = 1 + c, 
                c += 1, b_2 && (b_2.var *= -1 / 0))))]] = -3)) ]] ^= 1 < (b_2_2 && (b_2_2["function" in [ (c = 1 + c, 
                b_2 && (b_2[--b + (b_2_2 && "function" == typeof b_2_2.in && 0 <= --_calls_ && b_2_2.in(-0, /[a2][^e]+$/))] += (23..toString(), 
                0))), (c = 1 + c, c += 1, 1) ]] = 0 / 0 * (-2 !== 38..toString()))))] += !1), b_2_2 = 0 / 0;
            }
        }(null), function() {
            c = 1 + c, 24..toString(), 38..toString(), 24..toString(), 24..toString(), c = 1 + c, 
            38..toString(), c = 1 + c, 22 % [] - ([] >> null) || bar_2 && (bar_2[c = 1 + c, 
            (-4 ^ this ^ !1) <= -this - 0 / 0] /= 22);
        }();
    }(a++ + /[abc4]/g.exec(((c += 1) + (a++ + ([] in [ (c = 1 + c, (0 < (23..toString() == /[a2][^e]+$/)) >>> ("c" << 38..toString() <= !0)), (c = 1 + c, 
    38..toString() >>> "foo" < !1 || 0) ])) || b || 5).toString()));
    !function(foo_2, foo_2) {
        for (var key32, brake31 = 5; --b + b && 0 < --brake31; ) {
            for (key32 in --b + typeof (--b + void 0)) {
                c += 1;
            }
        }
        if ("function" == typeof f15 && 0 <= --_calls_ && f15()) {
            try {
                foo_2 && foo_2.NaN;
            } finally {
                c += 1;
            }
        }
    }();
}(((+(a && (a.in = (23..toString() <= -3) / !0 | !0 === (-0 & 24..toString()))) || a || 3).toString(), 
a++, a++, 23..toString(), a = 0 != (a && (a.undefined = 0 !== [])), a = (c += 1) + [ (c += 1) + ("function" == typeof a && 0 <= --_calls_ && a(24..toString())), a++ + (b ^= a), "function" == typeof f16 && 0 <= --_calls_ && f16({
    "-2": --b + typeof bar_1
}, -2 in {
    1.5: "function" == typeof a && 0 <= --_calls_ && a(23..toString()),
    0: (c += 1) + [ (c = 1 + c, +(-0 < (a && (a.b *= -5)))), (c = 1 + c, !0 ^ !1), (c = 1 + (c += 1), 
    (a && (a.Infinity = 4 % (-0 | this))) | (a && (a[(c += 1) + (38..toString(), -1)] = ("" == {}) * (a && (a.var += "number" <= 38..toString()))))) ][c = 1 + c, 
    a && (a[--b + void 0] += 0 - (this >> /[a2][^e]+$/) === (0 == (void 0 & {})))],
    Infinity: (c += 1) + ++a
}), (c += 1) + typeof (a++ + ("function" == typeof a && 0 <= --_calls_ && a("", 24..toString(), --a))) ])), bar = a && (a.c = NaN !== (2 === [ , 0 ].length) < !0 ^ (a && (a[c = 1 + c, 
NaN % (0 !== (a && (a[c = 1 + c, "0c" <= ("a" * 24..toString() ^ 23..toString() != (2 === [ , 0 ].length))] %= NaN)))] = 5) && this >>> ""));

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
evalmachine.<anonymous>:79
{var brake12 = 5; do {((typeof (bar_2)));} while (((a/* ignore */++)) && --brake12 > 0);}
                      ^

ReferenceError: bar_2 is not defined
    at f2 (evalmachine.<anonymous>:79:23)
    at f0 (evalmachine.<anonymous>:8:76)
    at evalmachine.<anonymous>:225:1
    at ContextifyScript.Script.runInContext (vm.js:59:29)
    at Object.runInContext (vm.js:120:6)
    at Object.exports.run_code (/home/runner/work/UglifyJS/UglifyJS/test/sandbox.js:65:12)
    at Object.<anonymous> (/home/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1337:33)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
uglified result:
null '12,9,false,3number' undefined 24 Infinity NaN undefined
```
```js

// reduced test case (output will differ)

// (beautified)
const bar_2 = f2();

function f2() {
    typeof bar_2;
}
// output: ReferenceError: bar_2 is not defined
// minify: 
// options: {
//   "mangle": false,
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  booleans
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index 647c97e1c01..d8f689c3ba8 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -7598,7 +7598,9 @@ merge(Compressor.prototype, {
                 // typeof always returns a non-empty string, thus it's
                 // always true in booleans
                 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
-                return (exp instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
+                return (exp instanceof AST_SymbolRef && all(exp.definition().orig, function(sym) {
+                    return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
+                }) ? make_node(AST_True, self) : make_sequence(self, [
                     exp,
                     make_node(AST_True, self)
                 ])).optimize(compressor);


Test patch:
diff --git a/test/compress.js b/test/compress.js
index 77b09f0decd..3d9b1a6398c 100644
--- a/test/compress.js
+++ b/test/compress.js
@@ -386,7 +386,7 @@ function test_case(test) {
             mangle: test.mangle
         });
         var actual = stdout[toplevel ? 1 : 0];
-        if (test.expect_stdout === true) {
+        if (test.expect_stdout === true || test.expect_stdout instanceof Error && test.expect_stdout.name === actual.name) {
             test.expect_stdout = actual;
         }
         if (!sandbox.same_stdout(test.expect_stdout, actual)) {
diff --git a/test/compress/const.js b/test/compress/const.js
index 694c9d16fb4..2f88975e105 100644
--- a/test/compress/const.js
+++ b/test/compress/const.js
@@ -1084,3 +1084,23 @@ issue_4231: {
     }
     expect_stdout: "function"
 }
+
+issue_4245: {
+    options = {
+        booleans: true,
+    }
+    input: {
+        const a = f();
+        function f() {
+            typeof a;
+        }
+    }
+    expect: {
+        const a = f();
+        function f() {
+            a,
+            1;
+        }
+    }
+    expect_stdout: true
+}
diff --git a/test/compress/let.js b/test/compress/let.js
index 8374db9a59a..deb4812044c 100644
--- a/test/compress/let.js
+++ b/test/compress/let.js
@@ -893,3 +893,26 @@ issue_4231: {
     expect_stdout: "function"
     node_version: ">=4"
 }
+
+issue_4245: {
+    options = {
+        booleans: true,
+    }
+    input: {
+        "use strict";
+        let a = f();
+        function f() {
+            typeof a;
+        }
+    }
+    expect: {
+        "use strict";
+        let a = f();
+        function f() {
+            a,
+            1;
+        }
+    }
+    expect_stdout: ReferenceError("a is not defined")
+    node_version: ">=4"
+}