Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(a_2, arguments_1) {
    var a_1 = [ --b + (arguments_1 = (c = c + 1) + (b = a)), arguments_1 && typeof arguments_1.b == "function" && --_calls_ >= 0 && arguments_1.b(typeof a_2 != "symbol") ], Infinity_2 = (b-- || 7).toString()[--b + (typeof arguments_1 == "function" && --_calls_ >= 0 && arguments_1((c = c + 1) + Infinity_2))];
    L26781: {
        const arguments_2 = false, a_1 = [ ,  ][{
            get b() {
                const Infinity_2 = typeof arguments;
                a++ + "function";
                {
                    return "function" in {
                        1.5: a++ + a--,
                        length: Infinity_2 /= void a
                    };
                }
            },
            Infinity: b++,
            1.5: (c = c + 1) + a++
        }.b], bar = --b + "function", arguments_1_1 = --b + (typeof bar == "function" && --_calls_ >= 0 && bar(3, "function"));
    }
}

var bar = f0();

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(a_2, arguments_1) {
    var a_1 = [ --b + (arguments_1 = (c += 1) + (b = a)), arguments_1 && "function" == typeof arguments_1.b && 0 <= --_calls_ && arguments_1.b("symbol" != typeof a_2) ], Infinity_2 = (b-- || 7).toString()[--b + ("function" == typeof arguments_1 && 0 <= --_calls_ && arguments_1((c += 1) + Infinity_2))];
    {
        const a_1 = [ ,  ][{
            get b() {
                var Infinity_2 = typeof arguments;
                return a++, "function" in {
                    1.5: a++ + a--,
                    length: Infinity_2 /= void 0
                };
            },
            Infinity: b++,
            1.5: (c += 1) + a++
        }.b], bar = --b + "function";
        --b, "function" == typeof bar && 0 <= --_calls_ && bar(3, "function");
    }
}

var bar = f0();

console.log(null, a, b, c, 1 / 0, NaN, void 0);
```
```
original result:
evalmachine.<anonymous>:15
length:((Infinity_2/=(void a))),
                   ^

TypeError: Assignment to constant variable.
    at Object.get b [as b] (evalmachine.<anonymous>:15:20)
    at f0 (evalmachine.<anonymous>:20:4)
    at evalmachine.<anonymous>:24:11
    at ContextifyScript.Script.runInContext (vm.js:59:29)
    at Object.runInContext (vm.js:120:6)
    at Object.exports.run_code (/home/runner/work/UglifyJS/UglifyJS/test/sandbox.js:65:12)
    at Object.<anonymous> (/home/runner/work/UglifyJS/UglifyJS/test/ufuzz/index.js:1324:33)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
uglified result:
null 102 97 2 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
{
    const a_1 = {
        get b() {
            const Infinity_2 = 0;
            return Infinity_2 /= 0;
        }
    }.b;
}
// output: TypeError: Assignment to constant variable.
// minify: 
// options: {
//   "mangle": false,
//   "validate": true
// }
```
```
minify(options):
{
  "mangle": false
}

Suspicious compress options:
  reduce_vars
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index e36fcaa7ba9..5dd10c6cbde 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -485,9 +485,10 @@ merge(Compressor.prototype, {
         }
 
         function safe_to_assign(tw, def, declare) {
-            if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
+            if (!(declare || all(def.orig, function(sym) {
                 return !(sym instanceof AST_SymbolConst);
-            });
+            }))) return false;
+            if (def.fixed === undefined) return true;
             if (def.fixed === null && def.safe_ids) {
                 def.safe_ids[def.id] = false;
                 delete def.safe_ids;
@@ -8557,7 +8558,9 @@ merge(Compressor.prototype, {
                         ]).optimize(compressor);
                     }
                 }
-            } else if (self.left instanceof AST_SymbolRef) {
+            } else if (self.left instanceof AST_SymbolRef && all(self.left.definition().orig, function(sym) {
+                return !(sym instanceof AST_SymbolConst);
+            })) {
                 var parent;
                 if (self.operator == "=" && self.left.equivalent_to(self.right)
                     && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {


Test patch:
diff --git a/test/compress/const.js b/test/compress/const.js
index b161e87657d..4411cbf84a7 100644
--- a/test/compress/const.js
+++ b/test/compress/const.js
@@ -963,3 +963,49 @@ issue_4210: {
     }
     expect_stdout: true
 }
+
+issue_4212_1: {
+    options = {
+        dead_code: true,
+    }
+    input: {
+        console.log({
+            get b() {
+                const a = 0;
+                return a /= 0;
+            }
+        }.b);
+    }
+    expect: {
+        console.log({
+            get b() {
+                const a = 0;
+                return a /= 0;
+            }
+        }.b);
+    }
+    expect_stdout: true
+}
+
+issue_4212_2: {
+    options = {
+        reduce_vars: true,
+    }
+    input: {
+        console.log({
+            get b() {
+                const a = 0;
+                return a /= 0;
+            }
+        }.b);
+    }
+    expect: {
+        console.log({
+            get b() {
+                const a = 0;
+                return a /= 0;
+            }
+        }.b);
+    }
+    expect_stdout: true
+}
diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js
index 3e2d865d788..e650a46f9ca 100644
--- a/test/ufuzz/index.js
+++ b/test/ufuzz/index.js
@@ -899,21 +899,22 @@ function getDotKey(assign) {
 function createAccessor(recurmax, stmtDepth, canThrow) {
     var namesLenBefore = VAR_NAMES.length;
     var s;
-    var prop1 = getDotKey();
-    if (rng(2) == 0) {
-        s = [
-            "get " + prop1 + "(){",
-            strictMode(),
-            createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
-            createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
-            "},"
-        ];
-    } else {
-        var prop2;
-        do {
-            prop2 = getDotKey();
-        } while (prop1 == prop2);
-        createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
+    createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
+        var prop1 = getDotKey();
+        if (rng(2) == 0) {
+            s = [
+                "get " + prop1 + "(){",
+                strictMode(),
+                defns(),
+                _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
+                createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
+                "},"
+            ];
+        } else {
+            var prop2;
+            do {
+                prop2 = getDotKey();
+            } while (prop1 == prop2);
             s = [
                 "set " + prop1 + "(" + createVarName(MANDATORY) + "){",
                 strictMode(),
@@ -922,8 +923,8 @@ function createAccessor(recurmax, stmtDepth, canThrow) {
                 "this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
                 "},"
             ];
-        });
-    }
+        }
+    });
     VAR_NAMES.length = namesLenBefore;
     return filterDirective(s).join("\n");
 }