Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

try {
    {
        var b_2 = function arguments_1() {
            async function f0(a_1) {
                function f1() {
                }
                var bar_1 = f1(Infinity);
                function f2() {
                    c = 1 + c, ("function" >= this, bar_1 += 4 ?? "a") <= ([ , 0 ].length === 2) % "undefined" * (24..toString() >> "function");
                    c = 1 + c, (-1 && 38..toString()) ^ 24..toString() % "object" | "foo" % 24..toString() >= "" << "object";
                }
                var arguments_1 = f2(1, (c = c + 1) + [ (c = 1 + c, ("foo" ^ this) / (a_1 && (a_1.done = 2 < 2)) >>> (23..toString() << this) % (NaN ^ 0)) ]);
                function f3(a, bar_1_1) {
                    const arguments_1 = (c = c + 1) + function c_1() {
                    }();
                    c = 1 + c, (-1 !== 2) ** ({} !== 2) ^ ("a" > "foo") / ("number" >> "foo");
                    c = 1 + c, ([ , 0 ].length === 2 ^ "c") > (22 & Infinity) ^ ("number" ^ 4) >= (5 | 2);
                }
                var c_2 = f3((c = 1 + c, c_2 += ((c_2 %= "function" << true) ^ 38..toString() + 24..toString()) < this / false - ("object" > 4)), ...[]);
                async function f4() {
                    c = 1 + c, ((3 ^ undefined) !== (-5 | -5)) + (("object" >= ([ , 0 ].length === 2)) - (false && /[a2][^e]+$/));
                    c = 1 + c, ([ , 0 ][1] == "number" != (c_2[c = 1 + c, ((23..toString() & Infinity) == -5 < ([ , 0 ].length === 2)) > ((arguments_1 && (arguments_1.b = -4 >> [ , 0 ][1])) >= (false & ""))] -= /[a2][^e]+$/ % 2)) >>> +(5 === "c");
                }
                var a = f4("a", a_1 && a_1.Infinity);
                async function f5() {
                    c = 1 + c, c_2 && (c_2.Infinity |= ("b" ^ "b", 38..toString() > /[a2][^e]+$/, -5 ^ "a", 
                    -3 !== 2));
                    c = 1 + c, ~(3 == /[a2][^e]+$/) === ((c_2 = NaN >= {}) ?? -1 | [ , 0 ][1]);
                }
                var async_2 = f5(a++ + a_1);
            }
            var c_2 = f0`${/[abc4]/.test(("b" - 0 << ([ , 0 ][1] || "foo") == (-1, null) < ("bar" ^ [ , 0 ][1]) || b || 5).toString())}`;
            function f6() {
                c = c + 1;
                {
                    var Infinity = function f7(Infinity_2) {
                        c = 1 + c, ({}, true) - (true >>> 0) | (c_2 && (c_2.a = -3 !== "")) >>> true + -5;
                        c = 1 + c, (c_2 && (c_2.foo = Infinity ^ -3)) !== (undefined != null) != (c_2 && (c_2.c = "b" ^ Number(0xdeadn << 16n | 0xbeefn) | (c = c + 1, 
                        "undefined")));
                    }(-1, [ , 0 ].length === 2);
                }
            }
            var async = f6(/[a2][^e]+$/);
        }();
    }
} catch (undefined) {
    var undefined = a++ + {
        length: undefined && undefined.c,
        0: --b + (b_2 && b_2[(c = c + 1) + (b += a)]),
        value: b++
    }[b_2 && typeof b_2.in == "function" && --_calls_ >= 0 && b_2.in(a++ + [][(c = c + 1) + (undefined && typeof undefined.Infinity == "function" && --_calls_ >= 0 && undefined.Infinity(23..toString()))], undefined)];
    try {
        --b + void function undefined() {}();
    } catch (a_2) {
        c = c + 1;
        {
            var async_2 = function arguments_2(foo) {
                {
                    var brake20 = 5;
                    do {
                    } while (a++ + (typeof foo == "function" && --_calls_ >= 0 && foo()) && --brake20 > 0);
                }
                return a++ + !(undefined && (undefined.null <<= ({} - 1 | "a" - "bar") >>> (c = c + 1, 
                -4 !== 25)));
            }({});
        }
    }
}

if (25) {
    c = c + 1;
}

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

try {
    var b_2 = function() {
        var c_2 = async function(a_1) {
            c = 1 + (c += 1), a_1 && (a_1.done = !1), 23..toString();
            c = 1 + c, 24..toString(), c = 1 + c, 38..toString(), 24..toString();
            var arguments_1 = void 24..toString(), c_2 = (c = 1 + c, c_2 += ((c_2 %= 0) ^ 38..toString() + 24..toString()) < this / !1 - !1, 
            void (c = 1 + (c = 1 + (c += 1))));
            a_1 && a_1.Infinity, c_2[c = 1 + (c = 1 + (c = 1 + c)), ((23..toString() & 1 / 0) == -5 < (2 === [ , 0 ].length)) > (0 <= (arguments_1 && (arguments_1.b = -4)))] -= NaN, 
            c = 1 + c, c_2 && (c_2.Infinity |= (38..toString(), !0)), c = 1 + c, c_2 = {} <= NaN;
        }`${/[abc4]/.test((!0).toString())}`;
        (function() {
            c = 1 + (c += 1), c_2 && (c_2.a = !0), c = 1 + c, c_2 && (c_2.foo = -3 ^ Infinity);
            var Infinity = void (c_2 && (c_2.c = "b" ^ Number(0xdeadn << 16n | 0xbeefn) | (c += 1, 
            "undefined")));
        })();
    }();
} catch (undefined) {
    var undefined = a++ + {
        length: undefined && undefined.c,
        0: --b + (b_2 && b_2[(c += 1) + (b += a)]),
        value: b++
    }[b_2 && "function" == typeof b_2.in && 0 <= --_calls_ && b_2.in(a++ + [][(c += 1) + (undefined && "function" == typeof undefined.Infinity && 0 <= --_calls_ && undefined.Infinity(23..toString()))], undefined)];
    try {
        --b;
    } catch (a_2) {
        c += 1;
        var async_2 = function(foo) {
            for (var brake20 = 5; a++ + ("function" == typeof foo && 0 <= --_calls_ && foo()) && 0 < --brake20; ) {}
            return a++ + !(undefined && (undefined.null <<= ({} - 1 | NaN) >>> (c += 1, !0)));
        }({});
    }
}

c += 1, console.log(null, a, b, c, 1 / 0, NaN, undefined);
```
```
original result:
null 100 10 18 Infinity NaN undefined

uglified result:
null 100 10 16 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var a = 0;

async function f0() {
    async function f4() {
        c_2;
    }
    f4();
    a++;
}

f0``;

console.log(a);
// output: 1
// 
// minify: 0
// 
// options: {
//   "mangle": false,
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false,
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  awaits
  inline
  reduce_vars
  unused
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index d5dc723f7c8..308a711b477 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -5414,7 +5414,6 @@ Compressor.prototype.compress = function(node) {
         def(AST_Node, return_true);
 
         def(AST_Constant, return_false);
-        def(AST_Destructured, return_true);
         def(AST_EmptyStatement, return_false);
         def(AST_Lambda, return_false);
         def(AST_ObjectIdentity, return_false);
@@ -5448,6 +5447,9 @@ Compressor.prototype.compress = function(node) {
             }
             return this.left.may_throw(compressor);
         });
+        def(AST_Await, function(compressor) {
+            return this.expression.may_throw(compressor);
+        });
         def(AST_Binary, function(compressor) {
             return this.left.may_throw(compressor)
                 || this.right.may_throw(compressor)
@@ -5484,6 +5486,12 @@ Compressor.prototype.compress = function(node) {
             return !this.optional && this.expression.may_throw_on_access(compressor)
                 || this.expression.may_throw(compressor);
         });
+        def(AST_ForEnumeration, function(compressor) {
+            if (this.init.may_throw(compressor)) return true;
+            var obj = this.object.tail_node();
+            if (!(obj instanceof AST_Array || obj.is_string(compressor))) return true;
+            return this.body.may_throw(compressor);
+        });
         def(AST_If, function(compressor) {
             return this.condition.may_throw(compressor)
                 || this.body && this.body.may_throw(compressor)
@@ -12931,7 +12939,11 @@ Compressor.prototype.compress = function(node) {
                 defined.set("arguments", true);
             }
             var async = is_async(fn);
-            if (async && !(compressor.option("awaits") && is_async(scope))) return;
+            if (async) {
+                if (!compressor.option("awaits")) return;
+                if (!is_async(scope)) return;
+                if (call.may_throw(compressor)) return;
+            }
             var names = scope.var_names();
             if (in_loop) in_loop = [];
             if (!fn.variables.all(function(def, name) {


Test patch:
diff --git a/test/compress/awaits.js b/test/compress/awaits.js
index 9dd9be2b595..96750dc0550 100644
--- a/test/compress/awaits.js
+++ b/test/compress/awaits.js
@@ -2605,3 +2605,59 @@ issue_5250: {
     ]
     node_version: ">=8"
 }
+
+issue_5258_1: {
+    options = {
+        awaits: true,
+        inline: true,
+    }
+    input: {
+        (async function() {
+            (async function() {
+                throw "FAIL";
+            })();
+            return "PASS";
+        })().catch(console.log).then(console.log);
+    }
+    expect: {
+        (async function() {
+            (async function() {
+                throw "FAIL";
+            })();
+            return "PASS";
+        })().catch(console.log).then(console.log);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=8"
+}
+
+issue_5258_2: {
+    options = {
+        awaits: true,
+        inline: true,
+    }
+    input: {
+        function f() {
+            throw "FAIL";
+        }
+        (async function() {
+            (async function() {
+                f();
+            })();
+            return "PASS";
+        })().catch(console.log).then(console.log);
+    }
+    expect: {
+        function f() {
+            throw "FAIL";
+        }
+        (async function() {
+            (async function() {
+                f();
+            })();
+            return "PASS";
+        })().catch(console.log).then(console.log);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=8"
+}