Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    {
        var b_2 = function undefined_1(b_2) {
            try {
            } catch (a) {
                {
                    var brake4 = 5;
                    do {
                        {
                            return --b + [ (c = 1 + c, (b_2 && (b_2.var = (22 | -0) / (2 - -2))) + (b_2 && (b_2.undefined += (23..toString() && {}, 
                            -24..toString())))), (c = 1 + c, (-5 >>> 25 || (c = c + 1, -1)) >> (b_2 && (b_2.c = Infinity >>> ([ , 0 ].length === 2) < ("undefined" || [ , 0 ][1])))) ];
                        }
                    } while (--b + {
                        NaN: b_2 = (c = 1 + c, c = c + 1, Infinity != null === 3 - NaN),
                        "\t": 0 === 1 ? a : b
                    } && --brake4 > 0);
                }
                {
                    switch ((c = c + 1) + typeof (c = 1 + c, ((false, false) ^ "number" >>> this) >> (-1 >> 22 === (b_2 && (b_2[c = 1 + c, 
                    ("function" >= -0) + (b_2 && (b_2.foo = -2 == {})) << (38..toString() / "number" || b_2 && (b_2[c = 1 + c, 
                    (-4 + 5 === 4 > Infinity) >> ("number" < 0 & this != "function")] += "" >>> null))] = "bar" << -0))))) {
                      case {
                            3: (c = 1 + c, ([ , 0 ][1] - -2 >> (2 > -3)) / (([] < 24..toString()) / ([ , 0 ][1] + 23..toString())))
                        }:
                        c = 1 + c, (b_2 += ("a" == this) % ("foo" - this)) == (-2 <= ([ , 0 ].length === 2)) + ("c" & false);
                        c = 1 + c, (-0 ^ "undefined") != (-5 != "bar") && undefined == 0 === (3 ^ "object");
                        break;

                      case (c = c + 1) + ((c = 1 + c, (b_2 && (b_2[--b + (typeof b === "string")] <<= -5 * undefined || NaN ^ "c")) / (true * "c" != (c = c + 1, 
                        true))) || 4).toString()[c = 1 + c, ("undefined" && "b") * (-0 < 5) && (c = c + 1, 
                        NaN - -1)]:
                        c = 1 + c, b_2 = -3 >>> "function" < (3 ^ -1) ^ (b_2 && (b_2[c = 1 + c, ("bar" >>> 24..toString() === (true === this)) >> delete (Infinity !== 25)] = ("" ^ 3) !== ("bar" == "object")));
                        c = 1 + c, void ((c = c + 1, "number") >= (this != "object"));
                        break;

                      case b_2 && typeof b_2.foo == "function" && --_calls_ >= 0 && b_2.foo(-4, (c = 1 + c, 
                        (1 && 38..toString()) / (Infinity * [ , 0 ][1]), ~(3 > this)), 38..toString()):
                        c = 1 + c, -3 << 38..toString() && 0 < {} || (undefined > NaN, 22 && /[a2][^e]+$/);
                        break;

                      case "foo" in [ (c = 1 + c, ~(-2 && -4) ^ (b_2 && (b_2.NaN = "function" % this)) <= ("b", 
                        "bar")), (c = 1 + c, b_2 && (b_2[-2 in {
                            in: (c = 1 + c, (-5 >>> -3 != (false !== "")) % (1 !== false == (undefined & true)))
                        }] = "a" % "function" && "object" != NaN) || {} >>> 38..toString() & "a" <= {}) ]:
                        break;
                    }
                    {}
                    {
                        var expr14 = typeof f2 == "function" && --_calls_ >= 0 && f2(1, (c = 1 + c, ("number" | "b") & (5 & 4) || ([ , 0 ].length === 2 || 24..toString()) !== (b_2 && (b_2.a = "bar" << 24..toString()))));
                        L13670: for (var key14 in expr14) {
                            c = 1 + c;
                            var Math_2 = expr14[key14];
                            try {
                                c = 1 + c, ("number" === "undefined" != (Infinity ^ /[a2][^e]+$/)) >> ([] >= 3) / (-1 * 24..toString());
                            } catch (NaN_2) {
                            }
                        }
                    }
                    if (--b + /[abc4]/.test(((c = 1 + c, "undefined" >> "" << ("c" | 2), 0 / {} <= -3 - this) || b || 5).toString())) {
                        var expr18 = (c = 1 + c, (([ , 0 ].length === 2 != -3) > (NaN & "object")) >> (2 <= 2 != (NaN == 5)));
                        for (var key18 in expr18) {
                            c = 1 + c;
                            var b_2 = expr18[key18];
                            c = 1 + c, (true >> -5, void {}) > ~("foo" * this);
                        }
                    } else {
                    }
                }
            }
            {
            }
        }();
    }
    {
        var brake23 = 5;
        do {
            for (var brake24 = 5; --b + typeof [ [ ~a, [ (c = 1 + c, b_2 && (b_2.c = (b_2 += "" > 3) >>> (-0 << false) & (NaN | -3) == true >> /[a2][^e]+$/)) ].length, (c = c + 1) + (b_2 && typeof b_2.length == "function" && --_calls_ >= 0 && b_2.length()), (c = c + 1) + !b, --b + {
                3: (c = 1 + c, (b_2 && (b_2.var = (c = c + 1, -3) << (b_2 && (b_2.foo = 22 - "function")))) / ("c" / -3 && (b_2 && (b_2.var = [] || "number")))),
                "\t": (c = 1 + c, (c = c + 1, [] ^ "foo") !== (-1 << -1 | 1 != 38..toString())),
                NaN: (c = 1 + c, (c = c + 1, "function") ^ "" < false | (0 * {} && (b_2 = NaN <= "c"))),
                in: (c = 1 + c, -5 + ([ , 0 ].length === 2) + (null < 1) == (this & -1) * (38..toString() && 2))
            }.length ] ][(c = c + 1) + (0 === 1 ? a : b)] && brake24 > 0; --brake24) {
                var b_1 = function f1(NaN, b_2) {
                    {
                        var brake26 = 5;
                        L13671: do {
                            {
                                var expr27 = a++ + typeof (c = c + 1, "a" - "bar" && (b_2 += {} ^ "number"));
                                L13672: for (b_2 in expr27) {
                                    {
                                        var Infinity_1 = function f2(b) {
                                        }();
                                    }
                                }
                            }
                        } while ((0 === 1 ? a : b) && --brake26 > 0);
                    }
                }(b += a, (c = c + 1) + (b_2 && b_2.in));
            }
        } while (--b + a++ && --brake23 > 0);
    }
}

var b = f0(a);

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    var b_2 = void 0, brake23 = 5;
    do {
        for (var brake24 = 5; --b + typeof [ [ ~a, [ (c = 1 + c, b_2 && (b_2.c = (b_2 += !1) >>> 0 & !1)) ].length, (c += 1) + (b_2 && "function" == typeof b_2.length && 0 <= --_calls_ && b_2.length()), (c += 1) + !b, --b + {
            3: (c = 1 + c, (b_2 && (b_2.var = (c += 1, -3 << (b_2 && (b_2.foo = NaN))))) / NaN),
            "\t": (c = 1 + c, c += 1, ("foo" ^ []) != (-1 << -1 | 1 != 38..toString())),
            NaN: (c = 1 + c, !1 ^ "function" | (0 * {} && (b_2 = !1))),
            in: (c = 1 + (c += 1), -5 + (2 === [ , 0 ].length) + !0 == (-1 & this) * (38..toString() && 2))
        }.length ] ][(c += 1) + b] && 0 < brake24; --brake24) {
            (function() {
                var brake26 = 5;
                do {
                    var expr27 = a++ + (c += 1, "number");
                    for (b_2 in expr27) {}
                } while (b && 0 < --brake26);
            })((b += a, c += 1, b_2 && b_2.in));
        }
    } while (--b + a++ && 0 < --brake23);
}

b = f0(a);

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
null 230 undefined 450 Infinity NaN undefined

uglified result:
null 230 undefined 479 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var b_2 = 0;

for (var brake24 = 5; [ console.log(b_2) ] && brake24; --brake24) {
    (function f1(b_2) {
        var expr27 = typeof 0;
        for (b_2 in expr27) {
            var Infinity_1 = 0;
        }
    })();
}
// output: 0
// 0
// 0
// 0
// 0
// 0
// 
// minify: 0
// 5
// 5
// 5
// 5
// 5
// 
// options: {
//   "mangle": false,
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  evaluate
  keep_fargs
  loops
  unused
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index 90dd0f27c97..bab390cfe5a 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4800,7 +4800,7 @@ merge(Compressor.prototype, {
                 return true;
             }
             if (node instanceof AST_ForIn) {
-                if (!compressor.option("loops")) return;
+                if (!drop_vars || !compressor.option("loops")) return;
                 if (!is_empty(node.body)) return;
                 if (node.init.has_side_effects(compressor)) return;
                 node.object.walk(tw);


Test patch:
diff --git a/test/compress/loops.js b/test/compress/loops.js
index 23d46de1628..e24b3712a15 100644
--- a/test/compress/loops.js
+++ b/test/compress/loops.js
@@ -985,3 +985,27 @@ issue_4075: {
     }
     expect_stdout: "PASS"
 }
+
+issue_4082: {
+    options = {
+        keep_fargs: "strict",
+        loops: true,
+        unused: true,
+    }
+    input: {
+        var a = "PASS";
+        (function(a) {
+            for (a in "foo")
+                var b;
+        })();
+        console.log(a);
+    }
+    expect: {
+        var a = "PASS";
+        (function(a) {
+            for (a in "foo");
+        })();
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}