Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

{
    var brake2 = 5;
    while (a++ + {
        undefined: a && a.value,
        3: void b
    } && --brake2 > 0) {
        var expr3 = (a || a || 3).toString();
        for (var key3 in expr3) {
            {
                var expr4 = ([ key3 ] = [ a++ + a++, (c = c + 1) + key3 ]);
                L18892: for (var key4 in expr4) {
                    {
                        var bar_1 = function f0() {
                            {}
                            if (b <<= a) {
                                try {
                                    {
                                        var brake11 = 5;
                                        do {
                                            if (--_calls_ >= 0 && new class a_2 {
                                                constructor(bar_1) {
                                                    this[c = 1 + c, (Object.isExtensible(key3) && (key3[c = 1 + c, (Object.isExtensible(bar_1) && (bar_1[c = 1 + c, 
                                                    (true * Infinity & -1 < -5) * (Object.isExtensible(key4) && (key4.null %= (-3 ^ Infinity) * ("bar" === 23..toString())))] ^= "b" <= "bar")) & (2 | "b"), 
                                                    (25 ^ 0) + (38..toString() | this)] = false > "undefined")) / (c = c + 1, 0) % (([ , 0 ].length === 2, 
                                                    []) % (key4 >>= 2 ^ "object"))] -= 38..toString() / -0;
                                                    this[c = 1 + c, (a_2 = ({} & []) + (-4 ^ 24..toString())) ^ (undefined << 25) / (Object.isExtensible(key3) && ([ key3.b ] = [ "b" !== "function" ]))] = 25 || this;
                                                }
                                                async(undefined_2, arguments_2, key3) {
                                                    this.get ^= 1 < -5;
                                                    arguments_2 = super.b;
                                                    this[c = 1 + c, "b" * "object" === ("number" & "object") != ([] | {} && "undefined" >= 38..toString())] <<= true & [ , 0 ][1];
                                                }
                                            }((c = 1 + c, 3 / 25 * (25 - 24..toString()) / ("function" & "a" & (0 ^ 3))))) {
                                                var expr13 = (c = 1 + c, ("undefined" > 38..toString() >= undefined % -1) * ((-3 == -1) + (3 - 4)));
                                                for (var [ key13 ] in expr13) {
                                                    c = 1 + c;
                                                    var b_2 = expr13[key13];
                                                    c = 1 + c, (25 === 3 & 5 % null) << (true * 2 ^ "function" > 23..toString());
                                                }
                                            }
                                        } while ((c = c + 1) + ~(Infinity + "number" >>> (c = c + 1, /[a2][^e]+$/)) && --brake11 > 0);
                                    }
                                } catch (b_1) {
                                    try {
                                        for (var brake16 = 5; (c = 1 + c, ((undefined | 3) > {} + 1) >>> (("number" === "b") < ("object" == "c"))) && brake16 > 0; --brake16) {
                                            c = 1 + c, (0 === "function") <= (b_1 && (b_1[c = 1 + c, ("foo" - 3) / (22 * 5) < (3 != 0) * (22 >= 5)] += 23..toString() & 5)) ^ (22 & 23..toString()) > "object" / this;
                                        }
                                    } catch (key4_1) {
                                        c = 1 + c, (key4_1 && (key4_1[key3 && typeof key3.undefined == "function" && --_calls_ >= 0 && key3.undefined("c", (c = 1 + c, 
                                        (-4 < {}) / (2 >>> Infinity) != (25 ^ -5) > 23..toString() * 4), (c = 1 + c, b_1 >>= ((key3 && (key3[c = 1 + c, 
                                        ((key4_1 = 5 ^ 5) <= "c" >> "") / ((5 & 25) << (5 & "bar"))] += "a" == "undefined")) & (false ^ -1)) > (b_1 && ({
                                            length: b_1[([ b_1 ] = [ (c = 1 + c, ("number" || "b" || 24..toString() >> -3) > (22 >= 24..toString()) << (23..toString() ^ 5)), (c = 1 + c, 
                                            (({}, -3) === -4 >>> NaN) >= (/[a2][^e]+$/ ^ "a" ^ (key3 && (key3.foo ^= "object" % 0)))) ])]
                                        } = {
                                            length: "function" + null & 22 !== 22
                                        }))))] = (key3 && (key3[c = 1 + c, (b_1 = (key4_1 >>>= "" <= -2) >>> 38..toString() / -5) < (-5 + 4 != 23..toString() - "object")] += -0 === -3)) | [] & "undefined")) !== 25 * "c" * (true && [ , 0 ][1]);
                                        c = 1 + c, ("foo" === 0) < (-2 || {}) != ("foo" >> []) % (3, true);
                                    }
                                    c = c + 1;
                                } finally {
                                    var a_1;
                                    try {
                                        {
                                            var brake23 = 5;
                                            while ((c = 1 + c, (-2 >>> null) % (4 == "bar") >> (key4 += this * 5 > (-5 ^ ""))) && --brake23 > 0) {
                                                c = 1 + c, (this ^ "") << (0 >>> null) ^ true <= true & "function" - 25;
                                            }
                                        }
                                    } finally {
                                        c = 1 + c, (-4 || Infinity, delete [ , 0 ][1]) >>> (a_1 = {} + -4 && -5 ^ 38..toString());
                                        c = 1 + c, (true !== true && (true || -0)) >>> ("bar" <= 4 & 5 * "number");
                                    }
                                }
                            }
                        }([ !function*() {
                            {
                                var expr6 = --b + key4;
                                for (var key6 in expr6) {
                                    var key3_1 = (c = 1 + c, ("undefined" > -3) * (3 ^ []) * (null >> NaN < 23..toString() * "function"));
                                }
                            }
                        }().next().value, b + 1 - .1 - .1 - .1 ], 1 === 1 ? a : b, b--);
                    }
                }
            }
        }
    }
}

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
for (var t, f, l = 10, o = 100, s = 10, r = 0, n = 5; o++ + {
    undefined: o && o.value,
    3: void 0
} && 0 < --n; ) {
    for (t in (o || 3).toString()) {
        for (f in ([ t ] = [ o++ + o++, (r += 1) + t ])) {
            !function f0() {
                var n, i;
                if (s <<= o) {
                    try {
                        n = 5;
                        do {
                            if (0 <= --l && new class a_2 {
                                constructor(n) {
                                    this[r = 1 + r, (Object.isExtensible(t) && (t[r = 1 + r, Object.isExtensible(n) && (n[r = 1 + r, 
                                    0 * (Object.isExtensible(f) && (f.null %= -0))] ^= !0), 25 + ("38" | this)] = !1)) / (r += 1, 
                                    0) % ([] % (f >>= 2))] -= "38" / -0, this[r = 1 + r, -28 ^ 0 / (Object.isExtensible(t) && ([ t.b ] = [ !0 ]))] = 25;
                                }
                                async(n, i, e) {
                                    this.get ^= !1, super.b, this[r = 1 + r, !1] <<= 0;
                                }
                            }((r = 1 + r, .12 / 0))) {
                                for (var [ e ] in r = 1 + r, -0) {
                                    r = 1 + r, r = 1 + r;
                                }
                            }
                        } while ((r += 1) + ~("Infinitynumber" >>> (r += 1, /[a2][^e]+$/)) && 0 < --n);
                    } catch (i) {
                        try {
                            r = 1 + r;
                        } catch (n) {
                            r = 1 + r, n && (n[t && "function" == typeof t.undefined && 0 <= --l && t.undefined("c", !1, (r = 1 + (r = 1 + r), 
                            i >>= (-1 & (t && (t[r = 1 + r, +((n = 0) <= 0)] += !1))) > (i && ({
                                length: i[([ i ] = [ (r = 1 + r, !1), (r = 1 + r, (0 ^ (t && (t.foo ^= NaN))) <= !1) ])]
                            } = {
                                length: 0
                            }))))] = 0 | (t && (t[r = 1 + r, (i = (n >>>= !1) >>> -7.6) < !0] += !1))), r = 1 + r;
                        }
                        r += 1;
                    } finally {
                        try {
                            for (i = 5; r = 1 + r, NaN >> (f += -5 < 5 * this) && 0 < --i; ) {
                                r = 1 + r;
                            }
                        } finally {
                            r = 1 + r, delete [ , 0 ][1], r = 1 + r;
                        }
                    }
                }
            }(function*() {
                for (var n in --s + f) {
                    r = 1 + r;
                }
            }().next().value, s--);
        }
    }
}

console.log(null, o, s, r, Infinity, NaN, void 0);
```
```
original result:
null 129 2147483644 509 Infinity NaN undefined

uglified result:
null 129 2147483644 599 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var expr3 = 0..toString();

for (var key3 in expr3) {
    new class a_2 {
        constructor() {
            a_2 = 0;
        }
    }();
}
// output: TypeError: Assignment to constant variable.
// minify: 
// options: {
//   "compress": {
//     "hoist_vars": true,
//     "keep_infinity": true,
//     "passes": 1000000,
//     "unsafe": true
//   },
//   "keep_fnames": true,
//   "toplevel": true,
//   "mangle": {
//     "v8": true
//   },
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "compress": {
    "hoist_vars": true,
    "keep_infinity": true,
    "passes": 1000000,
    "unsafe": true
  },
  "keep_fnames": true,
  "toplevel": true,
  "mangle": {
    "v8": true
  },
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  evaluate
  reduce_vars
  unsafe
```


Discussion:

Patch:
diff --git a/lib/ast.js b/lib/ast.js
index 28e437f7494..6057c9b5b59 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -1759,11 +1759,11 @@ var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
 
 var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
     $documentation: "Symbol defining a class",
-}, AST_SymbolLet);
+}, AST_SymbolConst);
 
 var AST_SymbolClass = DEFNODE("SymbolClass", null, {
     $documentation: "Symbol naming a class expression",
-}, AST_SymbolLet);
+}, AST_SymbolConst);
 
 var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
     $documentation: "Symbol naming the exception in catch",


Test patch:
diff --git a/test/compress/classes.js b/test/compress/classes.js
index c57842f9a68..d4047a8a8c8 100644
--- a/test/compress/classes.js
+++ b/test/compress/classes.js
@@ -1875,3 +1875,136 @@ issue_5015_3: {
     expect_stdout: "PASS"
     node_version: ">=4"
 }
+
+issue_5053_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        "use strict";
+        try {
+            console.log(new class A {
+                constructor() {
+                    A = 42;
+                }
+            }());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect: {
+        "use strict";
+        try {
+            console.log(new class A {
+                constructor() {
+                    A = 42;
+                }
+            }());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
+
+issue_5053_2: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        "use strict";
+        try {
+            console.log(new class A {
+                f() {
+                    A = 42;
+                }
+            }().f());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect: {
+        "use strict";
+        try {
+            console.log(new class A {
+                f() {
+                    A = 42;
+                }
+            }().f());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
+
+issue_5053_3: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        try {
+            console.log(new class A {
+                p = A = 42;
+            }().p);
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect: {
+        try {
+            console.log(new class A {
+                p = A = 42;
+            }().p);
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect_stdout: "PASS"
+    node_version: ">=12"
+}
+
+issue_5053_4: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        "use strict";
+        class A {
+            constructor() {
+                A = 42;
+            }
+        }
+        try {
+            console.log(new A());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect: {
+        "use strict";
+        class A {
+            constructor() {
+                A = 42;
+            }
+        }
+        try {
+            console.log(new A());
+        } catch (e) {
+            console.log("PASS");
+        }
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}