Repository: swc-project/swc (rust)

Issue:
Compress `hoist_props` removes needed property from object
### Describe the bug

SWC compress is removing properties from an object which are needed at runtime. In the example provided, the `neededProp` does not appear in the compiled output, although it is clearly needed. For instance, running this code with Node.js outputs the value `1`.

### Input code

```typescript
const props = {
  neededProp: 1,
  nestedProp: {
    getProps: () => props,
    getProp() {
      return this.getProps().neededProp;
    },
  },
};
console.log(props.nestedProp.getProp()); // should log: 1
```


### Config

_No response_

### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.3.100&code=H4sIAAAAAAAAA02OwQqEMAxE737FHFuQyl4t7jfsP9igC6WVJp4W%2F33TiuteAnmZycycEwu2kjfGhE8HJKJA4aVkxKNvgOUCVQAsJHXlEcZiep72%2Fv%2Bk%2FJQChWQvCbK%2B2V1GY90d45vwqH4dh%2B9m7ZQjuZgX0167u4L7BViPYQCveY8BKtW2X1chiU%2FNAAAA&config=H4sIAAAAAAAAA4WWTW%2FbMAyG7%2F0Vgc8dUvSww07DVhTbZZcdh0FQZNphK4mGRDUxiv730c6H40TyboneRxRJkZTf71ar6iWa6svqXX7KH9gzBK%2FtD7AdhChCo22E%2B1EVvdMhQjjz41rsPeu9rFXcdxBNwI6r%2B0nnuL%2B2I6t177VD89N1FFh0Dmkmg6GgmUYfBu0ofVy4wts49ySANvypJrfuAjVo0bcC%2FDkDgqx3FF5jpw3EdYu8TZu1pxqUozpZWfs6xrA%2BW6rOm%2F%2FeePAK0H23OsZf2kG8iqFiHVoYIqsgPj48PlSTErSPDQU39z5FeIIGPTxTGM0%2BI9j65g6EtNBq0z%2BdUnSbvdH9mXVZxDHVvykFA4NbB%2Bj%2BEgnJM7pR1YnJaUYzJ8TJbwkto7%2B6GEnMTYIcemz6eZSGXBcgxmvvJFvJgedMvKMaaJeXNkQWtF8WlY4KPUObKeqjW9bqLoJ600XCSfVjpMJRg54YaiW115UQXyMjSYPlgRp0rYzUY17FAIbxDQqb5VzZ7KOEXAZq2KS2HXv4loA3bZPm%2FHbYjxcn7mflLWFk1aRCeg7yKTfzej3LxeRjowJwCvmjXwh9ee%2FQpsoMDeWPfVpgGqnBJfk%2F29FL8yL3WcJSqSi8NDODQmzyaR9SBoGxcHSAOhkY8m4WgWJ6Isr4g6aR0ioAO2SzLRw%2FjEtq8pqUgi4EdZDUQlsfiaGrFoFnCZxLNXlkZI5tl%2FTYuw0VWvJkAnhLuWE8IXJRTEtAkPGz75aI5OvxBagLkEzfvDS0PSsmZccnJ1dF0l9iWbWWNhfjZ5reF8%2BH076dZshprN8doerwVk4fDUMNHN65z9UEnWb%2FwcbHP9mEG%2BNsCAAA

### SWC Info output

_No response_

### Expected behavior

The `neededProp` should appear in the compiled output, within the `props` object.

### Actual behavior

_No response_

### Version

1.3.100

### Additional context

_No response_

Pull request:
fix(es/minifier): Do not drop used properties
**Description:**


**Related issue:**

 - Closes #8692

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
index 575fbb414d..431316f6b2 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
@@ -956,6 +956,34 @@ impl Visit for ThisPropertyVisitor {
         }
     }
 
+    fn visit_callee(&mut self, c: &Callee) {
+        if self.should_abort {
+            return;
+        }
+
+        c.visit_children_with(self);
+
+        if self.should_abort {
+            return;
+        }
+
+        if let Callee::Expr(e) = c {
+            match &**e {
+                Expr::This(..) => {
+                    self.should_abort = true;
+                }
+
+                Expr::Member(e) => {
+                    if e.obj.is_this() {
+                        self.should_abort = true;
+                    }
+                }
+
+                _ => {}
+            }
+        }
+    }
+
     fn visit_member_expr(&mut self, e: &MemberExpr) {
         if self.should_abort {
             return;


Test patch:
diff --git a/crates/swc/tests/fixture/issues-8xxx/8692/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8692/input/.swcrc
new file mode 100644
index 0000000000..4087dadd0f
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8692/input/.swcrc
@@ -0,0 +1,63 @@
+{
+    "$schema": "http://json.schemastore.org/swcrc",
+    "jsc": {
+        "externalHelpers": false,
+        "parser": {
+            "syntax": "typescript",
+            "tsx": false,
+            "dynamicImport": true,
+            "decorators": true
+        },
+        "keepClassNames": true,
+        "target": "es2020",
+        "minify": {
+            "compress": {
+                "arguments": false,
+                "arrows": false,
+                "booleans": false,
+                "booleans_as_integers": false,
+                "collapse_vars": false,
+                "comparisons": false,
+                "computed_props": false,
+                "conditionals": false,
+                "dead_code": false,
+                "directives": false,
+                "drop_console": false,
+                "drop_debugger": false,
+                "evaluate": false,
+                "expression": false,
+                "hoist_funs": false,
+                "hoist_props": true,
+                "hoist_vars": false,
+                "if_return": false,
+                "join_vars": false,
+                "keep_classnames": false,
+                "keep_fargs": false,
+                "keep_fnames": false,
+                "keep_infinity": false,
+                "loops": false,
+                "negate_iife": false,
+                "properties": false,
+                "reduce_funcs": false,
+                "reduce_vars": false,
+                "side_effects": false,
+                "switches": false,
+                "typeofs": false,
+                "unsafe": false,
+                "unsafe_arrows": false,
+                "unsafe_comps": false,
+                "unsafe_Function": false,
+                "unsafe_math": false,
+                "unsafe_symbols": false,
+                "unsafe_methods": false,
+                "unsafe_proto": false,
+                "unsafe_regexp": false,
+                "unsafe_undefined": false,
+                "unused": false,
+                "const_to_let": false,
+                "pristine_globals": false
+            },
+            "mangle": false
+        }
+    }
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8692/input/input.ts b/crates/swc/tests/fixture/issues-8xxx/8692/input/input.ts
new file mode 100644
index 0000000000..63d1ad4ddf
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8692/input/input.ts
@@ -0,0 +1,10 @@
+const props = {
+    neededProp: 1,
+    nestedProp: {
+        getProps: () => props,
+        getProp() {
+            return this.getProps().neededProp;
+        },
+    },
+};
+console.log(props.nestedProp.getProp()); // should log: 1
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8692/output/input.ts b/crates/swc/tests/fixture/issues-8xxx/8692/output/input.ts
new file mode 100644
index 0000000000..1971d4a040
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8692/output/input.ts
@@ -0,0 +1,10 @@
+const props = {
+    neededProp: 1,
+    nestedProp: {
+        getProps: ()=>props,
+        getProp () {
+            return this.getProps().neededProp;
+        }
+    }
+};
+console.log(props.nestedProp.getProp());
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8692/config.json b/crates/swc_ecma_minifier/tests/fixture/issues/8692/config.json
new file mode 100644
index 0000000000..53c3bdfc57
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8692/config.json
@@ -0,0 +1,47 @@
+{
+    "arguments": false,
+    "arrows": false,
+    "booleans": false,
+    "booleans_as_integers": false,
+    "collapse_vars": false,
+    "comparisons": false,
+    "computed_props": false,
+    "conditionals": false,
+    "dead_code": false,
+    "directives": false,
+    "drop_console": false,
+    "drop_debugger": false,
+    "evaluate": false,
+    "expression": false,
+    "hoist_funs": false,
+    "hoist_props": true,
+    "hoist_vars": false,
+    "if_return": false,
+    "join_vars": false,
+    "keep_classnames": false,
+    "keep_fargs": false,
+    "keep_fnames": false,
+    "keep_infinity": false,
+    "loops": false,
+    "negate_iife": false,
+    "properties": false,
+    "reduce_funcs": false,
+    "reduce_vars": false,
+    "side_effects": false,
+    "switches": false,
+    "typeofs": false,
+    "unsafe": false,
+    "unsafe_arrows": false,
+    "unsafe_comps": false,
+    "unsafe_Function": false,
+    "unsafe_math": false,
+    "unsafe_symbols": false,
+    "unsafe_methods": false,
+    "unsafe_proto": false,
+    "unsafe_regexp": false,
+    "unsafe_undefined": false,
+    "unused": false,
+    "const_to_let": false,
+    "pristine_globals": false,
+    "defaults": true
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8692/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/8692/input.js
new file mode 100644
index 0000000000..63d1ad4ddf
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8692/input.js
@@ -0,0 +1,10 @@
+const props = {
+    neededProp: 1,
+    nestedProp: {
+        getProps: () => props,
+        getProp() {
+            return this.getProps().neededProp;
+        },
+    },
+};
+console.log(props.nestedProp.getProp()); // should log: 1
\ No newline at end of file
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/8692/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/8692/output.js
new file mode 100644
index 0000000000..1971d4a040
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/8692/output.js
@@ -0,0 +1,10 @@
+const props = {
+    neededProp: 1,
+    nestedProp: {
+        getProps: ()=>props,
+        getProp () {
+            return this.getProps().neededProp;
+        }
+    }
+};
+console.log(props.nestedProp.getProp());