Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0() {
    function f1(bar, await_2) {
        {
            return --b + a++;
        }
        --b + (bar && typeof bar.a == "function" && --_calls_ >= 0 && bar.a() || a || 3).toString();
    }
    var bar = f1("object");
    function f2(foo_1) {
        {
            {
                var Infinity = function f3(b_1 = -5, bar_1, a_1) {
                }((c = 1 + c, (0 >>> !0o644n) * (24..toString() >= this) > (("b" && "c") ^ 24..toString() >>> 24..toString())), (c = 1 + c, 
                (bar && (bar.foo = void Infinity && "a" << 2)) | (this ^ [] || (foo_1 = {} === -5))), Infinity);
            }
        }
        {
            var b = function b_1(b_2, bar_1, bar_2) {
                const await = async function foo_1() {
                    c = 1 + c, (NaN << "number") * (38..toString() > undefined) > (22 >= Infinity > (false ^ ""));
                    c = 1 + c, (true != 24..toString(), "object" & -1) >> (([ , 0 ][1], Infinity) >> ((-42n).toString() << 3));
                    c = 1 + c, ([ , 0 ][1] !== undefined) < (3 === 38..toString()) | (foo_1 && (foo_1.foo &= "undefined" || "b")) >> (b_2 = (-42n).toString() == 3);
                    c = 1 + c, c = c + 1, true << -3 << (-2 ^ this);
                };
                c = c + 1;
                switch (c = 1 + c, ((-1 != -1) >>> +/[a2][^e]+$/) * ((true || "object") > ("b" !== 25))) {
                  case c = 1 + c, (foo_1 && (foo_1.null = ("a" < "b") / void -2)) != (-1 != "number") >= (25 === 5):
                    ;
                    break;

                  case c = 1 + c, bar && (bar[[ (c = 1 + c, (true | 25) >>> ("undefined" >>> -1) | ([ , 0 ][1], 
                    "undefined") * ([ , 0 ][1] && "object")), (c = 1 + c, 22 / "undefined" > ([ , 0 ].length === 2 != false) <= ((-1 | "function") == 5 >>> -3)) ][typeof b_2 == "function" && --_calls_ >= 0 && b_2((c = 1 + c, 
                    ((c = c + 1, "number") === true - "foo") << (3 <= null != (Infinity === -5))), false, (c = 1 + c, 
                    (24..toString() ^ 0 || (foo_1 |= [ , 0 ][1] * ([ , 0 ].length === 2))) >= !24..toString() >>> ([ 3n ][0] > 2 & Infinity)))]] += (2 !== "") - "undefined" / {} == (await && (await.null += (-2) ** 24..toString())) >= ("foo" && [ , 0 ][1])):
                    ;
                    break;

                  default:
                    ;

                  case c = 1 + c, (3 ^ "foo") === (undefined !== [ , 0 ][1]) | ~(false >> /[a2][^e]+$/):
                    ;
                    break;
                }
            }();
        }
    }
    var undefined_2 = f2(undefined_2 = --b + -4, 23..toString());
    function f4() {
        if (typeof f6 == "function" && --_calls_ >= 0 && f6([ 3n ][0] > 2, a++ + (bar && typeof bar.a == "function" && --_calls_ >= 0 && bar.a()))) {
            var undefined_1;
        } else {
            return (c = c + 1) + (typeof f2 == "function" && --_calls_ >= 0 && f2((c = 1 + c, 
            23..toString() ** "object" - (c = c + 1, Number(0xdeadn << 16n | 0xbeefn)) || 1 > "bar" > (-3 || 1)), (c = 1 + c, 
            (/[a2][^e]+$/ < "foo") / ((-42n).toString() && "undefined") ^ (-3 & !0o644n) <= 5 ** 25)));
        }
        {
            var expr15 = --b + (undefined_2 && undefined_2[+function undefined_1_1() {
                c = 1 + c, (1 > "undefined") * ([ , 0 ][1] >= ([ , 0 ].length === 2)) == ("" ^ 23..toString()) - ("undefined" === 1);
                c = 1 + c, (25 >= {} ^ "number" / []) < ~(undefined_2 >>>= "number" << 38..toString());
                c = 1 + c, true % [ , 0 ][1] >>> ([], "object") || (Number(0xdeadn << 16n | 0xbeefn) ^ "undefined") <= (-3 ^ 0);
                c = 1 + c, (undefined_2 && (undefined_2[a--] += ("a" === "b") * (0 - {}))) ^ ("object", 
                38..toString()) > (undefined_1_1 && (undefined_1_1.in += NaN >>> 23..toString()));
            }()]);
            for (let key15 in expr15) {
                {
                    var expr20 = --b + (undefined_2 && undefined_2[delete ((-2 | -0) === -0 >> -2 | (undefined_1 >>>= 25 < "") !== 25 - "function")]);
                    for (const key20 in expr20) {
                        c = 1 + c;
                        var b_2 = expr20[key20];
                        c = c + 1;
                    }
                }
            }
        }
    }
    var b_2 = f4(--b + typeof -b);
    function f5(a_1, a_2, foo_1) {
        {
            {
                var brake23 = 5;
                while (((c = 1 + c, (true & undefined) >>> (a_2 && (a_2.undefined = (-42n).toString() ^ (-42n).toString())) !== 23..toString() <= -3 <= ("function" | null)) ? (c = 1 + c, 
                ((bar && (bar.Infinity <<= 4 % 25)) >>> ("object" < Number(0xdeadn << 16n | 0xbeefn))) / (("bar" <= "") << (Number(0xdeadn << 16n | 0xbeefn) >= -3))) : (c = 1 + c, 
                ((-0, "function") >> (this << 23..toString())) ** (-2 - "object" ^ null !== 38..toString()))) && --brake23 > 0) {
                    var expr24 = (c = 1 + c, ([ 3n ][0] > 2, []) % ([ 3n ][0] > 2 != Infinity) ^ "bar" >> -3 & ([ 3n ][0] > 2 && NaN));
                    for (bar in expr24) {
                        c = 1 + c;
                        var arguments_1 = expr24[bar];
                        c = 1 + c, ("function" && 4) + (([ 3n ][0] > 2) >>> "") >>> (([ 3n ][0] > 2) * -3 == (2 === "function"));
                    }
                }
            }
            var a_2_1 = a++ + a_2_1;
            try {
                if (c = 1 + c, a_1 && (a_1.Infinity /= (-0 + Number(0xdeadn << 16n | 0xbeefn) ^ (undefined | "undefined")) !== ("undefined" & [ , 0 ].length === 2 ^ void Infinity))) {
                    c = 1 + c, (23..toString() ^ -5) <= (3, /[a2][^e]+$/) != (2 << 2 ^ "b" >= "undefined");
                } else {
                    c = 1 + c, ("number" % !0o644n > (!0o644n !== "bar")) / ("a" * 1 ^ (-2 ^ "c"));
                }
            } finally {
                c = 1 + c, ((arguments_1 /= 23..toString() > 22) & (undefined ^ -3)) * ((23..toString() < 24..toString()) - (undefined >= -1));
                c = 1 + c, (5 ^ 23..toString()) % (true == 2) * (22 <= [ , 0 ][1], !1);
            }
        }
        try {
            try {
                for (var brake35 = 5; undefined_2 && brake35 > 0; --brake35) {
                    var brake36 = 5;
                    do {
                        c = 1 + c, "object" / 24..toString() >= ([ , 0 ].length === 2 & 23..toString()) >= (25 == [ , 0 ][1] !== "undefined" >>> NaN);
                    } while ((c = 1 + c, (5 !== "b") > 22 / Infinity != ("c" ^ "a") % (/[a2][^e]+$/ | "bar")) && --brake36 > 0);
                }
            } catch (await_2) {
                switch (c = 1 + c, 22 * Infinity >>> (undefined && this) > ("number" > "undefined") + (2 || "object")) {
                  case c = 1 + c, undefined_2.var += (-1 === 3) % (null >> "") || /[a2][^e]+$/ > (-42n).toString() ^ "" >> -5:
                    ;
                    break;

                  case c = 1 + c, (-3 != -0 ^ 23..toString() <= 4) / (await_2 = [ , 0 ][1] > 22 !== (c = c + 1, 
                    undefined)):
                    ;
                    break;

                  case c = 1 + c, (Infinity ^ false | "function" & undefined) + ("a" != 24..toString() != (this !== false)):
                    ;
                    break;

                  default:
                    ;
                }
                try {
                    c = 1 + c, await_2 && (await_2.foo |= ([ 3n ][0] > 2 <= 0 ^ "object" << null) !== ~([ , 0 ].length === 2 | -5));
                } catch {
                }
            }
        } catch (b_2) {
            return --b + /[abc4]/g.exec(((c = 1 + c, -(22 << 2) >= (({}, 2) && /[a2][^e]+$/ != 25)) || b || 5).toString());
            {
                var expr42 = a++ + typeof (c = 1 + c, ([ , 0 ][1], {}) === ("function" != "c") != ("foo" == 2) * ("bar" >= 25));
                L27389: for (var key42 in expr42) {
                    c = 1 + c;
                    var Infinity_1 = expr42[key42];
                    {
                        var expr43 = (c = 1 + c, ((c = c + 1, "b") - ("object" - -2)) % ((-4 !== NaN) > (foo_1 = "number" !== 5)));
                        for (var key43 in expr43) {
                            c = 1 + c;
                            var arguments_1 = expr43[key43];
                            c = 1 + c, ([ , 0 ][1] < undefined === (null !== !0o644n)) % (~[] - ([ , 0 ][1] < "number"));
                        }
                    }
                }
            }
        }
    }
    var async_2 = f5(undefined_2 && typeof undefined_2.undefined == "function" && --_calls_ >= 0 && undefined_2.undefined`${a++ + (0 === 1 ? a : b)}${0 === 1 ? a : b}`, a++ + +((-2 * 0 ^ Infinity >> "c") >>> (true >>> 25 > (-0 && 0))), "b");
}

var undefined_1 = f0(24..toString());

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```
// uglified code
// !!! beautify failed !!!
SyntaxError: Unexpected token: operator «&&»
    at JS_Parse_Error.get (eval at <anonymous> (/Users/runner/work/UglifyJS/UglifyJS/tools/node.js:1:1), <anonymous>:71:23)
    at writeln (/Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1643:57)
    at errorln (/Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1653:5)
    at try_beautify (/Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1660:9)
    at log (/Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1763:9)
    at /Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:2057:80
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (/Users/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1993:20)
    at Module._compile (node:internal/modules/cjs/loader:1108:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
    at Module.load (node:internal/modules/cjs/loader:973:32)
    at Function.Module._load (node:internal/modules/cjs/loader:813:14)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
    at node:internal/main/run_main_module:17:47
//
var _calls_=10,a=100,b=10,c=0;function f0(){var bar=--b+a++;function f2(foo_1){var Infinity=(c=1+c,(0>>>!0o644n)*(24..toString()>=this)>("c"^24..toString()>>>24..toString()),c=1+c,bar&&(bar.foo=void 0),void(this^[]||(foo_1=-5==={})));!function(b_2){async function await(){c=1+c,38..toString(),c=1+c,24..toString(),(-42n).toString(),c=1+c,38..toString(),await&&(await.foo&="undefined"),b_2=3==(-42n).toString(),c=1+c,c+=1}switch(0){case c=1+(c=1+(c+=1)),1!=(foo_1&&(foo_1.null=NaN)):case c=1+c,bar&&(bar[[25,(c=1+(c=1+c),(2===[,0].length!=0)<NaN<=!1)]["function"==typeof b_2&&0<=--_calls_&&b_2((c=1+c,(NaN==="number")<<(0!=(-5===Infinity))),!1,(c=1+(c+=1),(0^24..toString()||(foo_1|=0*(2===[,0].length)))>=!24..toString()>>>(2<3n&Infinity)))]]+=!0-"undefined"/{}==0<=(await.null+=(-2)**24..toString())):break;default:case c=1+c,-1:}}()}var undefined_2=f2(undefined_2=--b-4,23..toString());!function(){if(!("function"==typeof f6&&0<=--_calls_&&f6(2<3n,a+++(bar&&"function"==typeof bar.a&&0<=--_calls_&&bar.a()))))return c+=1,0<=--_calls_&&f2((c=1+c,23..toString()**"object"-(c+=1,Number(0xdeadn<<16n|0xbeefn))||!0),(c=1+c,(-42n).toString()));for(var key15 in--b+(undefined_2&&undefined_2[+function undefined_1_1(){c=1+c,23..toString(),c=1+c,undefined_2>>>="number"<<38..toString(),c=1+c,Number(0xdeadn<<16n|0xbeefn),c=1+c,undefined_2&&(undefined_2[a--]+=!1*(0-{})),38..toString(),undefined_1_1&&(undefined_1_1.in+=NaN>>>23..toString())}()])){var expr20=--b+(undefined_2&&undefined_2[!0]);for(const key20 in expr20){c=1+c;key20;c+=1}}}(--b);!function(a_1,a_2){for(var brake23=5;c=1+c,(0>>>(a_2&&(a_2.undefined=(-42n).toString()^(-42n).toString()))!==23..toString()<=-3<=0?(c=1+c,((bar&&(bar.Infinity<<=4))>>>("object"<Number(0xdeadn<<16n|0xbeefn)))/(!1<<(-3<=Number(0xdeadn<<16n|0xbeefn)))):(c=1+c,("function">>(this<<23..toString()))**(NaN^null!==38..toString())))&&0<--brake23;){var expr24=(c=1+c,[]%(2<3n!=1/0)^0&(2<3n&&NaN));for(bar in expr24)c=1+(c=1+c)}a++;try{c=1+c,a_1&&(a_1.Infinity/=(-0+Number(0xdeadn<<16n|0xbeefn)^0)!=("undefined"&2===[,0].length^void 0))?(c=1+c,23..toString()):c=1+c}finally{c=1+c,23..toString(),23..toString(),24..toString(),c=1+c,23..toString()}try{try{for(var brake35=5;undefined_2&&0<brake35;--brake35)for(var brake36=5;c=1+c,24..toString(),23..toString(),c=1+c,0<--brake36;);}catch(await_2){switch(!1){case c=1+(c=1+c),undefined_2.var+=/[a2][^e]+$/>(-42n).toString()^0:case c=1+c,(!0^23..toString()<=4)/(await_2=!1!==void(c+=1)):case c=1+c,+("a"!=24..toString()!=(!1!==this)):}try{c=1+c,await_2&&(await_2.foo|=(2<3n<=0^0)!=~(2===[,0].length|-5))}catch{}}}catch(b_2){return--b,/[abc4]/g.exec((c=1+c,(b||5).toString()))}}(undefined_2&&"function"==typeof undefined_2.undefined&&0<=--_calls_&&undefined_2.undefined`${a+++b}${b}`,0+a++)}var undefined_1=f0(24..toString());console.log(null,a,b,c,1/0,NaN,void 0);
```
```
original result:
null 103 7 28 Infinity NaN undefined

uglified result:
SyntaxError: Unexpected token '&&'
    at new Script (node:vm:99:7)
    at createScript (node:vm:260:10)
    at Object.runInThisContext (node:vm:308:10)
    at node:internal/process/execution:77:19
    at [stdin]-wrapper:6:22
    at evalScript (node:internal/process/execution:76:60)
    at node:internal/main/eval_stdin:29:5
    at Socket.<anonymous> (node:internal/process/execution:205:5)
    at Socket.emit (node:events:390:22)
    at endReadableNT (node:internal/streams/readable:1307:12)
```
```js
// reduced test case (output will differ)

// (beautified)
(function b_1() {
    const await = async function foo_1() {
        0(foo_1);
    };
    console.log(await);
})();
// output: function(){}
// 
// minify: SyntaxError: Unexpected token ')'
// options: {
//   "mangle": false,
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false,
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  functions
  reduce_vars
  unused
  varify
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index d4cf0da9dab..7a6a0bcb1a2 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -5854,13 +5854,7 @@ merge(Compressor.prototype, {
                             && var_defs[sym.id] == 1
                             && sym.assignments == 0
                             && (value instanceof AST_AsyncFunction || value instanceof AST_Function)
-                            && (sym.references.length ? all(sym.references, function(ref) {
-                                    return value === ref.fixed_value();
-                                }) : value === def.name.fixed_value())
-                            && (!value.name || (old_def = value.name.definition()).assignments == 0
-                                && (old_def.name == def.name.name || all(old_def.references, function(ref) {
-                                    return ref.scope.find_variable(def.name.name) === def.name.definition();
-                                })))
+                            && assigned_once(value, sym.references)
                             && can_declare_defun()
                             && can_rename(value, def.name.name)) {
                             AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
@@ -5902,14 +5896,11 @@ merge(Compressor.prototype, {
                         sym.eliminated++;
                     }
 
-                    function is_catch(node) {
-                        var sym = node.definition();
-                        return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
-                    }
-
-                    function can_rename(fn, name) {
-                        var def = fn.variables.get(name);
-                        return !def || fn.name && def === fn.name.definition();
+                    function assigned_once(fn, refs) {
+                        if (refs.length == 0) return fn === def.name.fixed_value();
+                        return all(refs, function(ref) {
+                            return fn === ref.fixed_value();
+                        });
                     }
 
                     function can_declare_defun() {
@@ -5919,6 +5910,22 @@ merge(Compressor.prototype, {
                             || parent instanceof AST_If;
                     }
 
+                    function can_rename(fn, name) {
+                        if (!fn.name) return !fn.variables.get(name);
+                        old_def = fn.name.definition();
+                        if (old_def.assignments > 0) return false;
+                        if (old_def.name == name) return true;
+                        if (name == "await" && is_async(fn)) return false;
+                        return all(old_def.references, function(ref) {
+                            return ref.scope.find_variable(name) === sym;
+                        });
+                    }
+
+                    function is_catch(node) {
+                        var sym = node.definition();
+                        return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
+                    }
+
                     function flush() {
                         if (side_effects.length > 0) {
                             if (tail.length == 0) {


Test patch:
diff --git a/test/compress/awaits.js b/test/compress/awaits.js
index 18012b482a7..777359919f6 100644
--- a/test/compress/awaits.js
+++ b/test/compress/awaits.js
@@ -1218,3 +1218,31 @@ issue_4598: {
     expect_stdout: "PASS"
     node_version: ">=8"
 }
+
+issue_4618: {
+    options = {
+        functions: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        console.log(typeof function() {
+            var await = async function f() {
+                console || f();
+            };
+            console.log;
+            return await;
+        }());
+    }
+    expect: {
+        console.log(typeof function() {
+            var await = async function f() {
+                console || f();
+            };
+            console.log;
+            return await;
+        }());
+    }
+    expect_stdout: "function"
+    node_version: ">=8"
+}