// original code
// (beautified)
var a = 100, b = 10, c = 0;
{
!function f0(eval_2) {
(c = c + 1) + /[abc4]/.test(((b += a) || b || 5).toString());
{
var brake4 = 5;
do {
if ((c = c + 1) + function Object_6() {
switch (a--) {
case a++ + a--:
{
!function f1(undefined_9) {
}((c = 1 + c, (c = c + 1, {} >>> {}) >>> 1 % -4 / "object" >>> -4));
}
break;
default:
{
c = 1 + c, 38..toString() * true % -2 * [] * -0 + -4 & 1 >= -4;
c = 1 + c, [] % true >> {} ^ 1, 5 & false >>> -2 || "foo";
c = 1 + c, "undefined" < 5 < undefined << -4 === "function", -4 >> -0 << {};
c = 1 + c, 5 * 38..toString() > 1 % "undefined" && 5 <= 22 >>> [] ^ -0;
}
{
return;
/[abc4]/.test(((c = 1 + c, (c = c + 1, 25) * 23..toString() >> 1 >= [] || 2 - "foo" !== -2) || b || 5).toString());
}
case --b + 1 - -0 > undefined ^ "foo" < "function" == undefined !== (c = c + 1, 22) >= new function() {
return Math;
}():
var isFinite_17;
break;
case 5 + -0 + ([ , 0 ].length === 2) % -4 <= "function" - false >> 25 % [] & (c = c + 1) + typeof isFinite_18 == "crap":
if (typeof eval_20) {
c = c + 1;
}
if ((c = c + 1) + !function() {
}()) {
var brake23 = 5;
while ((c = 1 + c, Object_6 = "foo" < 2 === /[a2][^e]+$/ & 23..toString() <= 0 == "undefined" * -5 + "bar") && --brake23 > 0) {
c = 1 + c, true >>> /[a2][^e]+$/ ^ NaN ^ [ , 0 ].length === 2 | 38..toString() <= NaN,
[ , 0 ][1] & true;
}
}
break;
}
try {
{
{
throw c = 1 + c, (c = c + 1, true) >>> NaN != "number" & (c = c + 1, -5) < "bar" < Infinity;
}
}
} catch (isFinite) {
switch ((c = c + 1) + 0) {
case a++:
c = 1 + c, -4 <= 24..toString() >> NaN >> Infinity ^ -4 != false < 24..toString() + "number";
break;
case --b + ((c = 1 + c, undefined >> {} + "object" | "bar" !== "foo" <= -0 >> "bar" || false) || a || 3).toString():
break;
case (c = c + 1) + /[abc4]/.test(((c = 1 + c, 25 ^ 23..toString() + "function" & undefined * null - ([ , 0 ].length === 2) == ([ , 0 ].length === 2) / 2) || b || 5).toString()):
c = 1 + c, 2 << [] << null | [ , 0 ].length === 2, "foo" === Infinity < [ , 0 ][1] - -1;
c = 1 + c, null >= -2 & (c = c + 1, 3) / (c = c + 1, 5 >> -0);
break;
case ((c = 1 + c, -1 && 3 ^ false === 3 * (isFinite_17 &= null & -1, "number" >> -2)) || a || 3).toString():
c = 1 + c, 2 - NaN ^ "bar" < -1 ^ 2 % "function" != -3 !== 5;
c = 1 + c, {} === "bar" || -2 > -4 ^ 5 | -2 << 3 <= -4;
break;
}
var decodeURI_35 = /[abc4]/.test(((c = 1 + c, -3 >>> 1 & -5 !== 5 && 22 / "object" || 5 < -1) || b || 5).toString());
} finally {
for (var brake36 = 5; --b && brake36 > 0; --brake36) {
var brake37 = 5;
do {
c = 1 + c, 2 >>> -5 + [] % -4 !== (isFinite_17 === true >= 1 ^ (c = c + 1, 25));
} while ((c = 1 + c, 1 >= null << -1 + 4 > "undefined" <= "object" | [] ^ NaN) && --brake37 > 0);
}
var a_40;
}
}()) {
switch ((c = c + 1) + ((eval_2 === 4 !== -0) - NaN >= -1 === (eval_2 %= -4 << 5) | NaN >> "bar" || a || 3).toString()) {
case a++ + (((c = c + 1) + /[abc4]/.test(((c = 1 + c, -2 & {} >= Infinity <= 2 >> undefined < true - 25 >> "undefined") || b || 5).toString()) ? --b : --b + 22 >= 4 | -0 % "function" > 23..toString() && true % [] >> 5 >> (c = 1 + c,
(eval_2 >>= -2 < Infinity % "foo" >= []) !== {} >> 22 != 4 ^ -2)) || a || 3).toString():
{
var brake43 = 5;
do {
c = c + 1;
} while (--b + (c = 1 + c, 2 % null !== NaN == 38..toString() - "bar" ^ "number" - (c = c + 1,
[])) && --brake43 > 0);
}
{
var brake45 = 5;
while ((--b + 1 === 1 ? a : b) && --brake45 > 0) {
c = 1 + c, [] === false == "function" % 23..toString() | -2 >> {} << 23..toString() & 0;
}
}
break;
case new function() {
return Math;
}():
break;
case (c = c + 1) + ((c = c + 1) + 0 && -1 * false == {} ^ "number" >= 22 % NaN - "undefined"):
switch (--b + 2 ^ 38..toString() < false > undefined == 3 != false && Infinity !== -2 ^ (c = 1 + c,
null <= "object" ^ 2, 24..toString() % true, -3 * 24..toString() ^ 25)) {
case a++ + (c = 1 + c, 4 % 22 <= "function" < /[a2][^e]+$/ === [ , 0 ][1] ^ -0 ^ 1 <= -4):
c = 1 + c, eval_2 = -4 !== [ , 0 ][1] > "number" <= 2 == ([ , 0 ].length === 2) === /[a2][^e]+$/ * -3 ^ -4;
c = 1 + c, 24..toString() | 0 !== "number" * 24..toString() ^ (c = c + 1, -1) && Infinity,
"function";
break;
default:
c = 1 + c, eval_2 = "bar" + 22 != 23..toString() % 2 % -2 & 2 > false % 4;
c = 1 + c, 38..toString() / "foo" > 2 << "number" && (c = c + 1, eval_2 >>= -3 ^ false);
case a++ + (c = 1 + c, false === [ , 0 ][1] >= "bar" <= 3 >= {} ^ "function" + true !== {}):
c = 1 + c, 24..toString() || 25 <= Infinity >> null % (c = c + 1, NaN & 22);
c = 1 + c, Infinity && 4 / (c = c + 1, 5) / -5 - -1 > 22 === null;
break;
case typeof (c = 1 + c, 2 >> "undefined" >= -2 | -3 >>> null >= undefined <= -2 >> []):
c = 1 + c, 1 ^ 2 || (eval_2 *= null && true) << null * Infinity >= 23..toString() * 24..toString();
break;
}
break;
default:
}
if (--b + "function") {
!function f2(foo_57) {
c = 1 + c, "bar" % -0 - 2 - 2 != 2 ^ "number" + "foo" ^ "foo";
c = 1 + c, Infinity & [] >>> 22 == null + -2 <= null / 38..toString() ^ NaN;
}((c = c + 1) + /[abc4]/.test(((c = 1 + c, 0 + undefined >= "bar", {} >= -3 ^ -3 && 1 % /[a2][^e]+$/) || b || 5).toString()));
}
{
var brake60 = 5;
do {
break;
} while (a++ + ++a && --brake60 > 0);
}
} else {
var undefined = a++ + /[abc4]/.test((--b + b-- || b || 5).toString()), Infinity_64 = 0 === 1 ? a : b;
}
} while ((c = c + 1) + new function() {
0 === 1 ? a : b;
return Math;
}() && --brake4 > 0);
}
}(a++ + null / 38..toString() + -0 >>> "number", -2 || -2 >> -4 >> "object" >>> a++ + ~b);
}
{
var brake65 = 5;
do {
{
!function f3(isNaN) {
function f4(Object_67) {
try {
if (a++) {
try {
c = 1 + c, Infinity !== 4 & [ , 0 ].length === 2 ^ 24..toString() ^ -2 != -4 ^ "undefined" % 4;
} catch (Object_67_72) {
} finally {
}
} else {
}
} catch (b_74) {
{
var brake76 = 5;
while ((c = 1 + c, true && NaN < 24..toString() + "undefined" >= "number" >> 23..toString() * null >>> "undefined") && --brake76 > 0) {
c = 1 + c, null % "function" == 22 & 22 * 23..toString() && 0 === -1 - -2;
}
}
}
}
f4();
}(--a);
}
} while (b + 1 - .1 - .1 - .1 && --brake65 > 0);
}
console.log(null, a, b, c);
```js
// uglified code
// (beautified)
var a = 100, b = 10, c = 0;
!function(eval_2) {
c += 1, /[abc4]/.test(((b += a) || b || 5).toString());
var brake4 = 5;
do {
if ((c += 1) + function() {
switch (a--) {
case a++ + a--:
c = 1 + c, c += 1;
break;
default:
return c = 1 + c, 38..toString(), c = 1 + c, c = 1 + c, c = 1 + c, 38..toString(),
undefined;
case 1 + --b - -0 > undefined ^ 1 == undefined != (c += 1, 22 >= new function() {
return Math;
}()):
var isFinite_17;
break;
case 5 + (2 === [ , 0 ].length) % -4 <= NaN >> 25 % [] & (c += 1) + typeof isFinite_18 == "crap":
if (c += 1, (c += 1) + !0) {
for (var brake23 = 5; c = 1 + c, !1 & 23..toString() <= 0 == "NaNbar" && --brake23 > 0; ) {
c = 1 + c, [ , 0 ].length, 38..toString(), [ , 0 ][1];
}
}
}
try {
throw c = 1 + c, "number" != (c += 1, 1) & (c += 1, !0);
} catch (isFinite) {
switch ((c += 1) + 0) {
case a++:
c = 1 + c, 24..toString(), 24..toString();
break;
case --b + (c = 1 + c, undefined >> {} + "object" | !0 || !1 || a || 3).toString():
break;
case (c += 1) + /[abc4]/.test((c = 1 + c, 25 ^ 23..toString() + "function" & null * undefined - (2 === [ , 0 ].length) == (2 === [ , 0 ].length) / 2 || b || 5).toString()):
c = 1 + c, [ , 0 ].length, [ , 0 ][1], c = 1 + c, c += 1, c += 1;
break;
case (c = 1 + c, 3 ^ !1 === 3 * (isFinite_17 &= 0, 0) || a || 3).toString():
c = 1 + c, c = 1 + c;
}
/[abc4]/.test((c = 1 + c, b || 5).toString());
} finally {
for (var brake36 = 5; --b && brake36 > 0; --brake36) {
var brake37 = 5;
do {
c = 1 + c, c += 1;
} while (c = 1 + c, !1 | NaN ^ [] && --brake37 > 0);
}
}
}()) {
switch ((c += 1) + ((4 === eval_2 !== -0) - NaN >= -1 === (eval_2 %= -128) | 0 || a || 3).toString()) {
case a++ + (((c += 1) + /[abc4]/.test((c = 1 + c, -2 & {} >= 1 / 0 <= 2 >> undefined < -24 || b || 5).toString()) ? --b : 22 + --b >= 4 | NaN > 23..toString() && !0 % [] >> 5 >> (c = 1 + c,
(eval_2 >>= !1 >= []) != {} >> 22 != 4 ^ -2)) || a || 3).toString():
var brake43 = 5;
do {
c += 1;
} while (--b + (c = 1 + c, 1 == 38..toString() - "bar" ^ "number" - (c += 1, [])) && --brake43 > 0);
for (var brake45 = 5; (1 + --b == 1 ? a : b) && --brake45 > 0; ) {
c = 1 + c, 23..toString(), 23..toString();
}
break;
case new function() {
return Math;
}():
break;
case (c += 1) + ((c += 1) + 0 && -0 == {} ^ !1):
switch (2 + --b ^ 38..toString() < !1 > undefined == 3 != 0 && !0 ^ (c = 1 + c,
24..toString(), -3 * 24..toString() ^ 25)) {
case a++ + (c = 1 + c, !1 === [ , 0 ][1] ^ -0 ^ !1):
c = 1 + c, eval_2 = -4 !== [ , 0 ][1] > "number" <= 2 == (2 === [ , 0 ].length) === NaN ^ -4,
c = 1 + c, 24..toString(), 24..toString(), c += 1;
break;
default:
c = 1 + c, eval_2 = "bar22" != 23..toString() % 2 % -2 & !0, c = 1 + c, 38..toString() / "foo" > 2 && (c += 1,
eval_2 >>= -3);
case a++ + (c = 1 + c, !1 == [ , 0 ][1] >= "bar" <= 3 >= {} ^ "function" + !0 !== {}):
c = 1 + c, 24..toString() || (c += 1), c = 1 + c, c += 1;
break;
case c = 1 + c, typeof (!0 | -3 >>> null >= undefined <= -2 >> []):
c = 1 + c;
}
}
--b, function(foo_57) {
c = 1 + c, c = 1 + c, 38..toString();
}((c += 1, /[abc4]/.test((c = 1 + c, {} >= -3 ^ -3 && NaN || b || 5).toString())));
var brake60 = 5;
do {
break;
} while (a++ + ++a && --brake60 > 0);
} else {
var undefined = a++ + /[abc4]/.test((--b + b-- || b || 5).toString());
}
} while ((c += 1) + new function() {
return Math;
}() && --brake4 > 0);
}(a++ + null / 38..toString() - 0 >>> "number");
var brake65 = 5;
do {
!function(isNaN) {
!function(Object_67) {
try {
if (a++) {
try {
c = 1 + c, [ , 0 ].length, 24..toString();
} catch (Object_67_72) {}
}
} catch (b_74) {
for (var brake76 = 5; c = 1 + c, NaN < 24..toString() + "undefined" >= "number" >> null * 23..toString() >>> "undefined" && --brake76 > 0; ) {
c = 1 + c, 23..toString();
}
}
}();
}(--a);
} while (b + 1 - .1 - .1 - .1 && --brake65 > 0);
console.log(null, a, b, c);
```js
original result:
null 101 96 46
uglified result:
null 101 96 82
minify(options):
{ compress: { warnings: false },
mangle: false,
fromString: true }
Suspicious compress options:
sequences
Local variable undefined is causing an issue, and those actual undefined aren't being transformed into void 0 for some reasons.
Reduced test case (-c):
var a = 100, b = 10, c = 0;
!function f0(eval_2) {
var brake4 = 5;
do {
if ((c = c + 1) + function Object_6() {
switch (a--) {
default:
c = 1 + c, 38..toString() * true % -2 * [] * -0 + -4 & 1 >= -4;
c = 1 + c, [] % true >> {} ^ 1, 5 & false >>> -2 || "foo";
c = 1 + c, "undefined" < 5 < undefined << -4 === "function", -4 >> -0 << {};
c = 1 + c, 5 * 38..toString() > 1 % "undefined" && 5 <= 22 >>> [] ^ -0;
return;
}
}()) {
(c = c + 1) + ((eval_2 === 4 !== -0) - NaN >= -1 === (eval_2 %= -4 << 5) | NaN >> "bar" || a || 3).toString();
a++ + (((c = c + 1) + /[abc4]/.test(((c = 1 + c, -2 & {} >= Infinity <= 2 >> undefined < true - 25 >> "undefined") || b || 5).toString()) ? --b : --b + 22 >= 4 | -0 % "function" > 23..toString() && true % [] >> 5 >> (c = 1 + c,
(eval_2 >>= -2 < Infinity % "foo" >= []) !== {} >> 22 != 4 ^ -2)) || a || 3).toString();
} else {
var undefined = a++ + /[abc4]/.test((--b + b-- || b || 5).toString()), Infinity_64 = 0 === 1 ? a : b;
}
} while ((c = c + 1), --brake4 > 0);
}(a++ + null / 38..toString() + -0 >>> "number", -2 || -2 >> -4 >> "object" >>> a++ + ~b);
console.log(null, a, b, c);
Reduced test case:
var c = 0;
switch (c--) {
default:
c = 1 + c, 5, 1;
c = 1 + c, 3;
}
console.log(c);
Thanks. There are actually two bugs in here, and my current work in progress just fixed the one you've mentioned.
I'm now trying to make a reduced test case that spews undefined instead of void 0, which is proving quite elusive.
@alexlamsl Just throwing the idea out there - the switch optimizations you've made are fairly aggressive. That's a good thing. But should there be a Compressor option switch=false to disable all AST_Switch optimizations? Switch opts can be enabled by default, although it would be nice if it could be user controlled. At the very least it would help narrow down the Suspicious compress option in fuzzing.
Such a compress option would be in the tradition of sequences, loops, conditionals and evaluate.
@kzc good idea - will fold that into #1762 as well.
Giving user more degrees of freedom is never a bad thing (I do enjoy watching gun-to-foot every now and then :sunglasses:)
@alexlamsl Like you mentioned before, such aggressive optimizations would not be possible without fuzzing. Fuzzing is like a thousand Internets testing your code.
In the future a new option for an advanced optimization could be introduced disabled by default, and we could fuzz it for a couple of weeks before enabling the option by default after we have a minimum level of confidence.
Fuzzing is like a thousand Internets testing your code
Especially when we don't get much actual testing / (meaningful) reports these days... :smirk:
In the future a new option for an advanced optimization could be introduced disabled by default, and we could fuzz it for a couple of weeks before enabling the option by default after we have a minimum level of confidence.
Sounds like a plan - less chance of getting angry letters is always A Good Thing :tm:
Especially when we don't get much actual testing / (meaningful) reports these days..
I know what we could do to improve the situation - if a user reports a bug in Uglify, they will get a 50% off coupon on their next Uglify purchase.
Since we are on a run, why not introduce UglifyJS@Home and harness the power of Internet-scale distributed computing to run test/ufuzz.js?