Repository: mishoo/UglifyJS (language: javascript)

Issue:
Invalid output when using global assignments

**Uglify version (`uglifyjs -V`)** 3.19.2

**JavaScript input**

```js
globalThis.Unused = {};

globalThis.Namespace = {};
globalThis.Namespace.Foo = {
	bar: false,
};
```

**The `uglifyjs` CLI command executed or `minify()` options used.**

`uglifyjs input.js --compress --beautify --output output.js`

**JavaScript output or error produced.**

```js
globalThis.Unused = {}, globalThis.Namespace.Foo = {
    bar: !(globalThis.Namespace = {})
};
```

Note in the output code there is an error as self.Namespace is not declared. For some reason the creation of Namespace has been moved to after it is used, so UglifyJS has transformed this from working code in to broken code.

If you comment out the line `globalThis.Unused = {};` then it works correctly - so it looks like UglifyJS encountering the first global ends up in a state which then causes it to process the second global incorrectly.


Discussion / hints:
The problem appears to be with the `collapse_vars` option. It can be worked around by passing false for that option, e.g.:

`uglifyjs input.js --compress collapse_vars=false --beautify --output output.js`

produces the following working output:

```js
globalThis.Unused = {}, globalThis.Namespace = {}, globalThis.Namespace.Foo = {
    bar: !1
};
```
Thanks for the detailed report &minus; investigating.

Fix patch:
diff --git a/lib/compress.js b/lib/compress.js
index 0fea284d93..f0d470fa70 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -2734,7 +2734,7 @@ Compressor.prototype.compress = function(node) {
                 if (node instanceof AST_Function) {
                     return compressor.option("ie") && node.name && lvalues.has(node.name.name);
                 }
-                if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
+                if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node);
                 if (node instanceof AST_PropAccess) {
                     var exp = node.expression;
                     if (compressor.option("unsafe")) {
@@ -2751,8 +2751,9 @@ Compressor.prototype.compress = function(node) {
                 }
                 if (node instanceof AST_Spread) return true;
                 if (node instanceof AST_SymbolRef) {
+                    var assign_direct = symbol_in_lvalues(node);
                     if (is_undeclared_ref(node) && node.is_declared(compressor)) return false;
-                    if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
+                    if (assign_direct) return !is_direct_assignment(node, parent);
                     if (side_effects && may_modify(node)) return true;
                     var def = node.definition();
                     return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
@@ -3618,7 +3619,7 @@ Compressor.prototype.compress = function(node) {
                 return true;
             }
 
-            function symbol_in_lvalues(sym, parent) {
+            function symbol_in_lvalues(sym) {
                 var lvalue = lvalues.get(sym.name);
                 if (!lvalue || all(lvalue, function(lhs) {
                     return !lhs;


Test patch:
diff --git a/test/compress/properties.js b/test/compress/properties.js
index cb4f3f6ed3..342433a9bb 100644
--- a/test/compress/properties.js
+++ b/test/compress/properties.js
@@ -1827,3 +1827,27 @@ issue_5682_sub_2: {
     }
     expect_stdout: "PASS"
 }
+
+issue_5927: {
+    options = {
+        collapse_vars: true,
+        evaluate: true,
+        pure_getters: "strict",
+        reduce_vars: true,
+    }
+    input: {
+        A = {};
+        while (console.log("PASS"));
+        A.p;
+        A.q = 42;
+        A.q.r = 42;
+    }
+    expect: {
+        A = {};
+        while (console.log("PASS"));
+        A.p;
+        A.q = 42;
+        A.q.r = 42;
+    }
+    expect_stdout: "PASS"
+}