Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(b_1, c_2, bar_2) {
    {
        var c_2_2 = function f1(Math_1) {
            --b + (typeof f3 == "function" && --_calls_ >= 0 && f3(~b, "object", 23..toString()));
            switch (--b + (c_2 && typeof c_2.null == "function" && --_calls_ >= 0 && c_2.null(-0, typeof f0 == "function" && --_calls_ >= 0 && f0()))) {
              case (c = c + 1) + (b /= a):
                var a = --b + (b_1 && b_1[Math_1 && Math_1.b]), foo = (c = c + 1) + (typeof bar_2 == "function" && --_calls_ >= 0 && bar_2("function", [] in [ (c = 1 + c, 
                c_2 && (c_2[--b + (typeof b_1 == "function" && --_calls_ >= 0 && b_1((c = 1 + c, 
                (Math_1 && (Math_1[--b + Math_1] += (c_2 && (c_2[c = 1 + c, 25 & 22 & [ , 0 ].length === 2 !== {} || (25 << /[a2][^e]+$/) / ("object" && "object")] &= "bar" !== "foo"), 
                "undefined" < 5))) * (({} === 4) << ("b" !== 3))), 22))] |= (Math_1 += ([ , 0 ].length === 2 !== -1) >>> (this != true)) ^ (38..toString() >= -2, 
                "undefined" < "a"))), (c = 1 + c, true << 22 < (bar_2 && (bar_2[c = 1 + c, (24..toString() + null | 24..toString() % this) + ("" / [] === -2 <= 22)] = Infinity << 4)) ^ (this | 1) > 25 % false), (c = 1 + c, 
                (this >= 24..toString()) << (Math_1 && (Math_1[c = 1 + c, "a" >>> this < (-3 === "c") | "a" % -5 % ("foo" <= this)] = -1 > false)) != (undefined == ([ , 0 ].length === 2) || -1 << /[a2][^e]+$/)) ]));
                break;

              case a++ + b++:
                try {
                    {
                        throw typeof Math_1 == "function" && --_calls_ >= 0 && Math_1(-0, true, (c = 1 + c, 
                        (({} ^ -2) & ([] || Infinity)) <= ((bar_2[c = 1 + c, (2 << -0) - (38..toString() ^ -5) == ("bar" >= Infinity & 2 < 0)] = -2 | -3) > Infinity + 4)));
                    }
                } catch (b_2) {
                    const b_1 = -!(-3 - 5);
                    {
                        var undefined_1 = function f2(a, undefined_1, bar_2) {
                        }((c = 1 + c, bar_2 && (bar_2[b--] += (/[a2][^e]+$/ ^ 4) % ("object" - "b") < (-5 + -5, 
                        "a" == Infinity))), -4);
                    }
                    switch (~b) {
                      case --b + (Math_1 && typeof Math_1.var == "function" && --_calls_ >= 0 && Math_1.var(null, this)):
                        c = 1 + c, (4 || null) / (undefined_1 = 5 - "object") ^ (b_1 && (b_1.b += ([ , 0 ].length === 2) * NaN > (-0 | 0)));
                        c = 1 + c, b_1 && (b_1.in = (([ , 0 ].length === 2) * false || (c = c + 1, -3)) > (true % "number" ^ (-0, 
                        Infinity)));
                        break;

                      case +b:
                        break;

                      default:
                        c = 1 + c, NaN % 4 >>> (Infinity < 22) === (b_1 && (b_1[{
                            c: (c = 1 + c, (c = c + 1, undefined === this) | ("" | 25) << (-4 ^ 0))
                        }] = (c = c + 1, Infinity) >>> (-2 === -0)));

                      case (c = c + 1) + +(void 22 >= (23..toString() <= "a") !== (Infinity >>> 24..toString() & (1 ^ -4))):
                    }
                }
                if (typeof c_2 == "function" && --_calls_ >= 0 && c_2()) {
                    try {
                        a++ + typeof (c = 1 + c, Math_1 && (Math_1[{
                            0: (c = 1 + c, (Infinity | [ , 0 ][1] || [ , 0 ][1] - "foo") >>> (bar_2 && (bar_2.in = 2 & [ , 0 ][1])) / (-1, 
                            -2)),
                            get foo() {
                                c = 1 + c, (Math_1 && (Math_1.foo += 2 >> -3), c = c + 1, 24..toString()) | ("" >= undefined) + ({} ^ "bar");
                            },
                            "": (c = 1 + c, (Math_1 = Infinity >>> [] >= ("bar" ^ "a")) >= (undefined_1 >>>= 22 - "bar" + ("object" && [ , 0 ].length === 2))),
                            var: (c = 1 + c, undefined_1 && (undefined_1.in ^= (-1 < 2) << ("b" & 4)) || (-4 >= 22) >> (undefined_1 && (undefined_1.in = 23..toString() >> "number")))
                        }] = (undefined_1 && (undefined_1.a += "" != "a" != (-0 !== "object"))) * (("c" == undefined) % (4 && "bar"))));
                    } finally {
                        if (c = 1 + c, (c = c + 1, Infinity >= NaN) >>> void (-0 + 5)) {
                            c = 1 + c, undefined_1 += (1 >> -5 || "bar" & "foo") <= (c_2 && (c_2.Infinity += ("c" && "undefined") <= ({} != "number")));
                        } else {
                            c = 1 + c, c = c + 1, void (0 | {});
                        }
                    }
                } else {
                    var brake20 = 5;
                    do {
                        {
                            return (c = c + 1) + (typeof f3 == "function" && --_calls_ >= 0 && f3(-4));
                        }
                    } while (a++ + [] && --brake20 > 0);
                }
                break;

              case --b + [ , {
                    Infinity: a++ + /[abc4]/.test(((c = 1 + c, Math_1 && (Math_1.in = this ^ 23..toString() | [] >>> 23..toString()) || (-5 << -0) - (false !== "foo")) || b || 5).toString()),
                    var: typeof f2 == "function" && --_calls_ >= 0 && f2("a", 2),
                    null: --b + (typeof f2 == "function" && --_calls_ >= 0 && f2((c = 1 + c, NaN + "object" >= (null, 
                    25) !== ("number" < -1 & this * 24..toString())), "undefined", "function")),
                    "\t": (c = c + 1) + [ (c = 1 + c, (null != ([ , 0 ].length === 2) ^ (c_2 && (c_2[c = 1 + c, 
                    ("foo" + 4, true >>> 2) <= ((1, 24..toString()) ^ NaN & -0)] -= null >= NaN))) > ("function" % 5 <= -5 % 3)), (c = 1 + c, 
                    "object" > "a" != ("object" | 24..toString()) || (5 ^ "undefined") != (true, "a")), ,  ][c = 1 + c, 
                    (-5 ^ "number" && 24..toString() != NaN) != ((undefined_1 && (undefined_1[c = 1 + c, 
                    c = c + 1, ([ , 0 ].length === 2) + "function" >= (this === -4)] = 3 != "")) | (this, 
                    -3))],
                    0: typeof (c = 1 + c, (false < "b" != (-0 ^ undefined)) % ((null !== true) * (3 > 2)))
                }.NaN, a++ + {
                    get c() {
                        c = 1 + c, (undefined_1 = (this ^ /[a2][^e]+$/) * (25 >>> 1)) !== ("a" | 38..toString()) < (1 && "undefined");
                        {
                            return c = 1 + c, ("c" <= 23..toString() & "bar" !== "undefined") !== (-5 == 25) / (undefined_1 && (undefined_1[c = 1 + c, 
                            (c_2 && (c_2[/[abc4]/g.exec(((c = 1 + c, (c_2 && (c_2.Infinity += -3 && -2), null !== []) << (this !== "undefined", 
                            4 - "number")) || b || 5).toString())] = 22 <= [ , 0 ][1] === ("a" || 5))) === (+"b" != ("a" && 2))] ^= 3 || -2));
                        }
                    }
                }, b = a, ,  ][Math_1 |= typeof foo_2 === "unknown"]:
                break;

              case [ a++ + (typeof f2 == "function" && --_calls_ >= 0 && f2("number", (c = c + 1) + {
                    null: (c = 1 + c, (bar_2 && (bar_2.null = (this <= "b") % ("c" ^ [ , 0 ].length === 2))) > (2 >> 22 != (-3 & -0))),
                    "\t": (c = 1 + c, undefined / 23..toString() & (true | -0) ^ (null << "undefined") + (24..toString() && 2)),
                    NaN: (c = 1 + c, NaN << this <= (24..toString() || /[a2][^e]+$/) != (c_2 && (c_2.b = (c_2 && (c_2.a += -4 / "a")) | (false || 3))))
                })), a++ + /[abc4]/g.exec(((c = c + 1) + --b || b || 5).toString()) ][b -= a]:
                break;
            }
        }(-3, --b, Infinity);
    }
    if (function a() {
        {
            var brake25 = 5;
            while (--b + [ a++ + [ , 0 ][1], (c = c + 1) + (c = c + 1, (2 < undefined) + (24..toString() < /[a2][^e]+$/)), (0 ^ /[a2][^e]+$/ ^ 5 % "number") === (bar_2 = (false || undefined) === (bar_2 = (5, 
            [ , 0 ].length === 2))), --b + 2, (c = c + 1) + (0 === 1 ? a : b) ][--b + b_1] && --brake25 > 0) {
                --b + +function NaN_2() {
                }();
            }
        }
    }) {
        const c_2 = c_2_2;
    }
}

var b = f0();

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(b_1, c_2, bar_2) {
    {
        const c_2 = function(Math_1) {
            switch (--b, "function" == typeof f3 && 0 <= --_calls_ && f3(~b, "object", 23..toString()), 
            --b + (c_2 && "function" == typeof c_2.null && 0 <= --_calls_ && c_2.null(-0, "function" == typeof f0 && 0 <= --_calls_ && f0()))) {
              case (c += 1) + (b /= a):
                var a = --b + (b_1 && b_1[Math_1 && Math_1.b]);
                c += 1, "function" == typeof bar_2 && 0 <= --_calls_ && bar_2("function", [] in [ (c = 1 + c, 
                c_2 && (c_2[--b + ("function" == typeof b_1 && 0 <= --_calls_ && b_1((c = 1 + c, 
                (Math_1 && (Math_1[--b + Math_1] += (c_2 && (c_2[c = 1 + c, 16 & 2 === [ , 0 ].length !== {} || NaN] &= !0), 
                !1))) * ((4 === {}) << !0)), 22))] |= (Math_1 += (2 === [ , 0 ].length !== -1) >>> (1 != this)) ^ (38..toString(), 
                !1))), (c = 1 + c, !0 << 22 < (bar_2 && (bar_2[c = 1 + c, (24..toString() + null | 24..toString() % this) + ("" / [] === !0)] = 0)) ^ NaN < (1 | this)), (c = 1 + c, 
                (this >= 24..toString()) << (Math_1 && (Math_1[c = 1 + c, "a" >>> this < !1 | NaN % ("foo" <= this)] = !1)) != (null == (2 === [ , 0 ].length) || -1)) ]);
                break;

              case a++ + b++:
                try {
                    throw "function" == typeof Math_1 && 0 <= --_calls_ && Math_1(-0, !0, ((-2 ^ {}) & []) <= ((bar_2[c = 1 + (c = 1 + c), 
                    2 - (-5 ^ 38..toString()) == 0] = -1) > 1 / 0));
                } catch (b_2) {
                    const b_1 = -0;
                    var undefined_1 = (c = 1 + c, void (bar_2 && (bar_2[b--] += !1)));
                    switch (~b) {
                      case --b + (Math_1 && "function" == typeof Math_1.var && 0 <= --_calls_ && Math_1.var(null, this)):
                        c = 1 + c, undefined_1 = NaN, b_1 && (b_1.b += 0 < NaN * (2 === [ , 0 ].length)), 
                        c = 1 + c, b_1 && (b_1.in = 0 < (!1 * (2 === [ , 0 ].length) || (c += 1, -3)));
                        break;

                      case +b:
                        break;

                      default:
                        c = 1 + c, b_1 && (b_1[{
                            c: (c = 1 + c, c += 1, void 0 === this | 25 << -4)
                        }] = (c += 1, 0));

                      case (c += 1) + +(23..toString() <= "a" <= void 0 !== (1 / 0 >>> 24..toString() & -3)):
                    }
                }
                if ("function" == typeof c_2 && 0 <= --_calls_ && c_2()) {
                    try {
                        a++, c = 1 + c, Math_1 && (Math_1[{
                            0: (c = 1 + c, NaN >>> (bar_2 && (bar_2.in = 0)) / -2),
                            get foo() {
                                c = 1 + c, Math_1 && (Math_1.foo += 0), c += 1, 24..toString();
                            },
                            "": (Math_1 = 0 <= 1 / 0 >>> []) >= (undefined_1 >>>= NaN + (2 === [ , 0 ].length)),
                            var: (c = 1 + (c = 1 + c), undefined_1 && (undefined_1.in ^= 1) || !1 >> (undefined_1 && (undefined_1.in = 23..toString() >> "number")))
                        }] = NaN * (undefined_1 && (undefined_1.a += !1)));
                    } finally {
                        c = 1 + c, c = 1 + (c += 1), c += 1;
                    }
                } else {
                    var brake20 = 5;
                    do {
                        return (c += 1) + ("function" == typeof f3 && 0 <= --_calls_ && f3(-4));
                    } while (a++ + [] && 0 < --brake20);
                }
                break;

              case --b + [ , {
                    Infinity: a++ + /[abc4]/.test((c = 1 + c, (Math_1 && (Math_1.in = this ^ 23..toString() | [] >>> 23..toString()) || -6).toString())),
                    var: "function" == typeof f2 && 0 <= --_calls_ && f2("a", 2),
                    null: --b + ("function" == typeof f2 && 0 <= --_calls_ && f2((c = 1 + c, !1 !== (!1 & this * 24..toString())), "undefined", "function")),
                    "\t": (c += 1) + [ (c = 1 + c, !1 < (null != (2 === [ , 0 ].length) ^ (c_2 && (c_2[c = 1 + c, 
                    0 <= (0 ^ 24..toString())] -= !1)))), (c = 1 + c, 1 != ("object" | 24..toString()) || !0), ,  ][c = 1 + c, 
                    NaN != 24..toString() != (-3 | (undefined_1 && (undefined_1[c = 1 + c, c += 1, (-4 === this) <= (2 === [ , 0 ].length) + "function"] = !0)))],
                    0: (c = 1 + c, "number")
                }.NaN, a++ + {
                    get c() {
                        return c = 1 + c, undefined_1 = 12 * (this ^ /[a2][^e]+$/), 38..toString(), c = 1 + c, 
                        ("c" <= 23..toString() & !0) != !1 / (undefined_1 && (undefined_1[c = 1 + c, !0 === (c_2 && (c_2[/[abc4]/g.exec((c = 1 + c, 
                        c_2 && (c_2.Infinity += -2), ((null !== []) << NaN || b || 5).toString()))] = !1))] ^= 3));
                    }
                }, b = a, ,  ][Math_1 |= "unknown" == typeof foo_2]:
              case [ a++ + ("function" == typeof f2 && 0 <= --_calls_ && f2("number", (c += 1) + {
                    null: (c = 1 + c, !1 < (bar_2 && (bar_2.null = (this <= "b") % ("c" ^ 2 === [ , 0 ].length)))),
                    "\t": (c = 1 + c, void 0 / 23..toString() & 1 ^ 0 + (24..toString() && 2)),
                    NaN: (c = 1 + c, NaN << this <= (24..toString() || /[a2][^e]+$/) != (c_2 && (c_2.b = 3 | (c_2 && (c_2.a += NaN)))))
                })), a++ + /[abc4]/g.exec(((c += 1) + --b || b || 5).toString()) ][b -= a]:
            }
        }(-3, --b);
    }
}

b = f0();

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
null 100 undefined 8 Infinity NaN undefined

uglified result:
evalmachine.<anonymous>:1
var _calls_=10,a=100,b=10,c=0;function f0(b_1,c_2,bar_2){{const c_2=function(Math_1){switch(--b,"function"==typeof f3&&0<=--_calls_&&f3(~b,"object",23..toString()),--b+(c_2&&"function"==typeof c_2.null&&0<=--_calls_&&c_2.null(-0,"function"==typeof f0&&0<=--_calls_&&f0()))){case(c+=1)+(b/=a):var a=--b+(b_1&&b_1[Math_1&&Math_1.b]);c+=1,"function"==typeof bar_2&&0<=--_calls_&&bar_2("function",[]in[(c=1+c,c_2&&(c_2[--b+("function"==typeof b_1&&0<=--_calls_&&b_1((c=1+c,(Math_1&&(Math_1[--b+Math_1]+=(c_2&&(c_2[c=1+c,16&2===[,0].length!=={}||NaN]&=!0),!1)))*((4==={})<<!0)),22))]|=(Math_1+=(2===[,0].length!==-1)>>>(1!=this))^(38..toString(),!1))),(c=1+c,!0<<22<(bar_2&&(bar_2[c=1+c,(24..toString()+null|24..toString()%this)+(""/[]===!0)]=0))^NaN<(1|this)),(c=1+c,(this>=24..toString())<<(Math_1&&(Math_1[c=1+c,"a">>>this<!1|NaN%("foo"<=this)]=!1))!=(null==(2===[,0].length)||-1))]);break;case a+++b++:try{throw"function"==typeof Math_1&&0<=--_calls_&&Math_1(-0,!0,((-2^{})&[])<=((bar_2[c=1+(c=1+c

ReferenceError: c_2 is not defined
    at evalmachine.<anonymous>:1:170
    at f0 (evalmachine.<anonymous>:1:3254)
    at evalmachine.<anonymous>:1:3266
    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 /home/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1344:37
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (/home/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1336:20)
    at Module._compile (module.js:653:30)
```
```js
// reduced test case (output will differ)

// (beautified)
function f0(c_2) {
    var c_2_2 = function f1() {
        switch (0) {
          case b:
            return 0;

          case c_2:
          case typeof f2:
        }
    }();
    {
        const c_2 = c_2_2;
    }
}

var b = f0();
// output: 
// minify: ReferenceError: c_2 is not defined
// options: {
//   "mangle": false,
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  collapse_vars
  conditionals
  dead_code
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index f6de86567bb..bc01ebc3f2b 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -1494,14 +1494,22 @@ merge(Compressor.prototype, {
             }
 
             function handle_custom_scan_order(node, tt) {
+                if (!(node instanceof AST_BlockScope)) return;
+                // Skip (non-executed) functions
+                if (node instanceof AST_Scope) return node;
+                // Stop upon collision with block-scoped variables
+                if (node.variables && !node.variables.all(function(def) {
+                    return !lvalues.has(def.name);
+                })) {
+                    abort = true;
+                    return node;
+                }
                 // Scan object only in a for-in statement
                 if (node instanceof AST_ForIn) {
                     node.object = node.object.transform(tt);
                     abort = true;
                     return node;
                 }
-                // Skip (non-executed) functions
-                if (node instanceof AST_Scope) return node;
                 // Scan first case expression only in a switch statement
                 if (node instanceof AST_Switch) {
                     node.expression = node.expression.transform(tt);


Test patch:
diff --git a/test/compress/const.js b/test/compress/const.js
index 28dda019cf1..d1164cc173e 100644
--- a/test/compress/const.js
+++ b/test/compress/const.js
@@ -872,3 +872,40 @@ issue_4202: {
     }
     expect_stdout: "42"
 }
+
+issue_4205: {
+    options = {
+        collapse_vars: true,
+    }
+    input: {
+        var a = function(b) {
+            var c = function() {
+                switch (0) {
+                  case a:
+                    return 0;
+                  case b:
+                  case console.log("PASS"):
+                }
+            }();
+            {
+                const b = c;
+            }
+        }();
+    }
+    expect: {
+        var a = function(b) {
+            var c = function() {
+                switch (0) {
+                  case a:
+                    return 0;
+                  case b:
+                  case console.log("PASS"):
+                }
+            }();
+            {
+                const b = c;
+            }
+        }();
+    }
+    expect_stdout: true
+}