This one is pretty specific in that it requires bin/uglifyjs -c passes=2,keep_fargs=0 to trigger.
// original code
// (beautified)
var a = 100, b = 10, c = 0;
c = c + 1;
for (var brake2 = 5; b + 1 - .1 - .1 - .1 && brake2 > 0; --brake2) {
try {
{
var foo_2 = function f0(a_1) {
NaN;
{
var expr6 = --b + typeof (a++ + [ , ].undefined);
for (var key6 in expr6) {
var NaN_1 = a_1 && a_1.null, NaN_1 = [ b !== a, b == a, b = a ].null;
}
}
}((c = c + 1) + --b);
}
} catch (b_1) {
{
var expr8 = (c = c + 1) + void function a_2() {
if (~a) {
{
return a++ + {
"-2": (c = 1 + c, (foo_2 === "bar" >>> "number") * (([ , 0 ].length === 2) * -3) ^ -4 + -1 >= ("undefined" || [ , 0 ][1]))
}[(c = 1 + c, ((b_1 && (b_1.null /= {} <= -3)) ^ (-5 ^ /[a2][^e]+$/)) & (undefined - 23..toString()) * (0 <= "foo"))];
}
try {
{
var expr13 = (c = 1 + c, a_2 && (a_2[23..toString()] <<= -1 < 3 <= (1 !== /[a2][^e]+$/) > (true & []) << true * 5));
L42319: for (var key13 in expr13) {
c = 1 + c;
var b_1 = expr13[key13];
c = 1 + c, ((23..toString() === -1) >= ("function", /[a2][^e]+$/)) >> ((b_1 && (b_1.var = undefined >>> "")) >= (NaN <= [ , 0 ][1]));
}
}
} finally {
c = 1 + c, (b_1 = (24..toString(), "") > (NaN & -3)) >> (38..toString() / "" !== ([ , 0 ].length === 2 != []));
c = 1 + c, (a_2 != (-3 <= "object") >>> ({} ^ "foo")) <= (void -3 == 0 * -0);
}
} else {}
(c = c + 1) + {
"-2": --b + [ (c = 1 + c, (null != -5) - (-4 >> 4) != (null >>> -3 || "" >> 2)), (c = 1 + c,
a_2 && (a_2[a++ + (a_2 && a_2.b)] >>= (true ^ NaN ^ "foo" !== -0) % (foo_2 && foo_2.foo != "function" / null < ("" && 38..toString())))), (c = 1 + c,
(bar_2 /= true ^ 23..toString()) ^ "object" == "foo" && (c = c + 1, "foo") === "undefined" <= /[a2][^e]+$/), (c = 1 + c,
(-"" === "bar" * "function") / ((25 || "object") | ~0)) ],
"": (c = c + 1) + /[abc4]/.test((-a || b || 5).toString()),
c: a++ + (++a ? foo_2 && foo_2.foo : b == a)
}.length;
}();
for (var key8 in expr8) {
c = 1 + c;
var bar_2 = expr8[key8];
}
}
switch (b + 1 - .1 - .1 - .1) {
default:
c = c + 1;
case delete ((0 || 22 || "bar" + 25) / ((true | []) >>> (-2 >= {}))):
break;
case b == a:
switch (a++ + new function() {}()) {
case (c = c + 1) + (--b + ((-2 || undefined) !== (null && 0) || 38..toString() / -0 >> 1 / 23..toString())):
case a++ + [ a++ + (b &= a), a++ + [ b_1 && b_1.foo ].length ][1 === 1 ? a : b]:
break;
case (c = c + 1) + {
0: bar_2 && bar_2.null,
"": --b + {}[b === a],
Infinity: --b + [ typeof (c = 1 + c, ("function" && -4) < ("bar" != "object") & (null !== "" ^ (b_1 && (b_1.in = "function" == -1)))), typeof (c = 1 + c,
(Infinity || 38..toString()) >>> ([ , 0 ][1] & Infinity) == (foo_2 && (foo_2.length += 3 >= 1)) + (Infinity > 4)), /[abc4]/.test(((c = 1 + c,
(Infinity | null) - (b_1 == 1 < -5) > (("bar" ^ -5) != (3, "function"))) || b || 5).toString()), (c = c + 1) + !function bar_2() {
}() ],
foo: (c = c + 1) + function foo() {
return c = 1 + c, -0 % 22 + 2 / -3 && "foo" + Infinity - (-4 + 24..toString());
{
}
{
var expr25 = (c = 1 + c, ((2, "") >= -1 % 0) >> (Infinity ^ -0 | ([ , 0 ].length === 2) % 25));
L42320: for (var key25 in expr25) {
c = 1 + c, 2 << "object" !== (-3 & "object") ^ (NaN, 25) <= "object" - "object";
}
}
{
var brake27 = 5;
while ((c = 1 + c, ((bar_2 >>= 24..toString() || 4) | 0 * "object") << ((b_1 && b_1.foo != "foo" >>> 23..toString()) <= ([ , 0 ][1] | "bar"))) && --brake27 > 0) {
c = 1 + c, (-3 << undefined < (38..toString(), 25)) >> (b_1 >>>= {} | {} && (c = c + 1,
23..toString()));
}
}
}
}[/[a2][^e]+$/]:
{
var brake29 = 5;
while (typeof undefined_2 === "unknown" && --brake29 > 0) {
switch (a++ + ((2 & 4 ^ /[a2][^e]+$/ & 0) == -2 << [] << (c = c + 1, 2))) {
case 1 === 1 ? a : b:
{
}
{
}
break;
default:
{
}
case +function parseInt_2() {
c = 1 + c, (2 == 2) + ("foo" <= NaN) << (bar_2 && (bar_2[(c = 1 + c, (1 % 5 || [] !== "") > ({} == -3) >> ({} << -4))] /= undefined > -2)) / ("object" - 4);
}():
for (var brake35 = 5; (c = 1 + c, ([ , 0 ][1] ^ "number") < 4 / false, ([ , 0 ][1] && "foo") | "object" + ([ , 0 ].length === 2)) && brake35 > 0; --brake35) {
c = 1 + c, c = c + 1, (1 ^ [ , 0 ][1]) >= (3 <= 38..toString());
}
break;
case typeof c_2 == "special":
switch (c = 1 + c, (24..toString() && 22) >= (false < 5) != ({} > "object" === ("undefined" && 24..toString()))) {
case c = 1 + c, (1 << "" && (b_1 >>>= "bar" % 1)) >= -4 - "undefined" + (true >>> true):
;
break;
case c = 1 + c, foo_2 = (NaN >= 24..toString() > (38..toString() !== "function")) << (c = c + 1,
undefined) / (4 ^ 22):
;
default:
;
case c = 1 + c, (-4 >> "" >>> (undefined << [ , 0 ][1])) * ((b_1 && b_1.length === (2 & 24..toString())) & 1 < 1):
;
}
break;
}
}
}
break;
case a++ + -1:
c = c + 1;
}
a++ + 5;
break;
case a++ + (0 === 1 ? a : b):
break;
}
} finally {
{
var brake40 = 5;
do {
for (var brake41 = 5; --b + void ((("bar", [ , 0 ].length === 2) !== -2 << -0) * (bar_2 && (bar_2.a %= -3 & -5) || -23..toString())) && brake41 > 0; --brake41) {
var foo_1;
}
} while (a++ + /[abc4]/.test((a++ + {
undefined: (c = c + 1) + (b = a),
length: --b + !("object" + {} ^ /[a2][^e]+$/ == 23..toString(), c = c + 1, "" - ([ , 0 ].length === 2)),
in: (c = c + 1) + (bar_2 && bar_2.null),
a: b--
} || b || 5).toString()) && --brake40 > 0);
}
}
}
console.log(null, a, b, c);
```js
// uglified code
// (beautified)
var a = 100, b = 10, c = 0;
c += 1;
for (var brake2 = 5; b + 1 - .1 - .1 - .1 && brake2 > 0; --brake2) {
try {
var foo_2 = function() {
var c = --b + typeof (a++ + [ , ].undefined);
for (var o in c) {
var r = a_1 && a_1.null, r = [ b !== a, b == a, b = a ].null;
}
}((c += 1, --b));
} catch (o) {
var expr8 = (c += 1) + void function o() {
if (~a) {
return a++ + {
"-2": (c = 1 + c, (0 === foo_2) * (-3 * (2 === [ , 0 ].length)) ^ !1)
}[(c = 1 + c, (-5 ^ (r && (r.null /= {} <= -3))) & !1 * (void 0 - 23..toString()))];
var r;
}
c += 1, --b, c = 1 + c, c = 1 + c, o && (o[a++ + (o && o.b)] >>= 0 % (foo_2 && 0 != foo_2.foo)),
c = 1 + c, !1 ^ (bar_2 /= !0 ^ 23..toString()) && (c += 1), c = 1 + c, c += 1, /[abc4]/.test((-a || b || 5).toString()),
a++, ++a && foo_2 && foo_2.foo;
}();
for (var key8 in expr8) {
c = 1 + c;
var bar_2 = expr8[key8];
}
switch (b + 1 - .1 - .1 - .1) {
default:
c += 1;
case !0:
break;
case b == a:
switch (a++ + new function() {}()) {
case (c += 1) + (--b + !0):
case a++ + [ a++ + (b &= a), a++ + [ o && o.foo ].length ][a]:
break;
case (c += 1) + {
0: bar_2 && bar_2.null,
"": --b + {}[b === a],
Infinity: --b + [ (c = 1 + c, typeof (!0 & (!0 ^ (o && (o.in = !1))))), (c = 1 + c,
typeof (1 / 0 >>> ([ , 0 ][1] & 1 / 0) == (foo_2 && (foo_2.length += !0)) + !0)), /[abc4]/.test((c = 1 + c,
0 - (0 == o) > !0 || b || 5).toString()), (c += 1) + !0 ],
foo: (c += 1) + function() {
return c = 1 + c, "foo" + 1 / 0 - (-4 + 24..toString());
}
}[/[a2][^e]+$/]:
for (var brake29 = 5; "unknown" == typeof undefined_2 && --brake29 > 0; ) {
switch (a++ + (0 == -2 << [] << (c += 1, 2))) {
case a:
break;
default:
case +function() {
c = 1 + c, bar_2 && (bar_2[(c = 1 + c, 1 > (-3 == {}) >> ({} << -4))] /= !1);
}():
for (var brake35 = 5; c = 1 + c, ([ , 0 ][1] && "foo") | "object" + (2 === [ , 0 ].length) && brake35 > 0; --brake35) {
c = 1 + c, c += 1, 38..toString();
}
break;
case "special" == typeof c_2:
switch (c = 1 + c, (24..toString() && 22) >= !0 != ({} > "object" === 24..toString())) {
case c = 1 + c, (o >>>= NaN) >= NaN:
break;
case c = 1 + c, foo_2 = (NaN >= 24..toString() > ("function" !== 38..toString())) << void (c += 1) / 18:
default:
case c = 1 + c, (-4 >>> (void 0 << [ , 0 ][1])) * (!1 & (o && o.length === (2 & 24..toString()))):
}
}
}
break;
case a++ - 1:
c += 1;
}
a++;
break;
case a++ + b:
}
} finally {
var brake40 = 5;
do {
for (var brake41 = 5; --b + void (bar_2 && (bar_2.a %= -7) || 23..toString()) && brake41 > 0; --brake41) {
var foo_1;
}
} while (a++ + /[abc4]/.test((a++ + {
undefined: (c += 1) + (b = a),
length: --b + (23..toString(), c += 1, !("" - (2 === [ , 0 ].length))),
in: (c += 1) + (bar_2 && bar_2.null),
a: b--
} || b || 5).toString()) && --brake40 > 0);
}
}
console.log(null, a, b, c);
```js
original result:
null 155 153 81
uglified result:
null 165 163 101
minify(options):
{ compress: { keep_fargs: false, passes: 3 } }
Suspicious compress options:
passes
pure_getters
reduce_vars
unused
Perhaps you've already isolated it, but notice the removal of the function f0 argument a_1 even though it is still referenced:
original:
for (var brake2 = 5; b + 1 - .1 - .1 - .1 && brake2 > 0; --brake2) {
try {
{
var foo_2 = function f0(a_1) {
NaN;
{
var expr6 = --b + typeof (a++ + [ , ].undefined);
for (var key6 in expr6) {
var NaN_1 = a_1 && a_1.null, NaN_1 = [ b !== a, b == a, b = a ].null;
}
}
}((c = c + 1) + --b);
}
} catch (b_1) {
bin/uglifyjs bug.js -c passes=2,keep_fargs=0 -b bracketize
for (var brake2 = 5; b + 1 - .1 - .1 - .1 && brake2 > 0; --brake2) {
try {
var foo_2 = function() {
var expr6 = --b + typeof (a++ + [ , ].undefined);
for (var key6 in expr6) {
var NaN_1 = a_1 && a_1.null, NaN_1 = [ b !== a, b == a, b = a ].null;
}
}((c += 1, --b));
} catch (b_1) {
@kzc thanks for looking into it :+1:
I am working on #1821 (again) so haven't started investigating this yet.
Reduced test case:
$ cat bug.js
var b = 10;
try {
!function(arg) {
for (var key in "hi") {
var n = arg.baz, n = [ b = 42 ];
}
}(--b);
} catch (ex) {}
console.log(b);
$ node bug.js
42
md5-5aa8b70ec666ca70883896e7506688f2
$ bin/uglifyjs bug.js -c passes=2,keep_fargs=0 -b bracketize | node
9
md5-5aa8b70ec666ca70883896e7506688f2
$ bin/uglifyjs bug.js -c passes=2,keep_fargs=0 -b bracketize
var b = 10;
try {
!function() {
for (var key in "hi") {
var n = arg.baz, n = [ b = 42 ];
}
}(--b);
} catch (ex) {}
console.log(b);
It's a strange bug - clearly there's a reference to the argument arg, but it was dropped.
Disabling reduce_vars fixes it:
$ bin/uglifyjs bug.js -c passes=2,keep_fargs=0,reduce_vars=1 -b bracketize | node
9
$ bin/uglifyjs bug.js -c passes=2,keep_fargs=0,reduce_vars=0 -b bracketize | node
42
With your example passes=2 is not required to trigger this bug.
I know about keep_fargs, reduce_vars & unused being the culprit - searching for others to complete the picture.
The missing piece of the puzzle being pure_getters=strict - now onward to locate and fix the bug :wink:
The problem is that n is unused and should be eliminated, but somehow drop_unused() couldn't manage to get rid of it (or the whole var statement for that matter).
n is used twice within the same variable declaration. I had a similar problem with collapse_vars a year ago.
Hahahahahaha... I've been reading this line for god knows how many times and I never stop and think for a second what it meant:
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
So yeah, easy fix.
n is used twice within the same variable declaration. I had a similar problem with collapse_vars a year ago.
It's only declared twice - drop_unused() can handle that just fine :wink:
Even collapse_vars wouldn't have trouble with it, as declarations won't be added to the SymbolRef.references array.
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
Oh crap, I think I may have introduced that.
As it turns out I was thinking of a different but similar commit I had done: https://github.com/mishoo/UglifyJS2/commit/3ac24219322384539caae803482ea257e7971cf2
In that case you are even safer - that section of code no longer exists on current master :wink: