Repository: swc-project/swc (language: rust)

Issue:
helper `set` is incorrectly renamed when it needs protection
### Describe the bug

The set function reassigns itself internally. 
When `eval` exists within the scope, the set function's name should be protected, but it's incorrectly renamed to `set1`, causing the helpers to fail to execute properly.

It was introduced by #9546 

### Input code

```typescript
class A {
	world = false
}

eval("hello")

class B extends A {
	constructor() {
		super()
	}

	set hello(v) {
		super.world = v
	}

	get hello() {
		return super.world
	}
}
```


### Config

_No response_

### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.7.37-nightly-20241021.1&code=H4sIAAAAAAAAA03NPQ6DMAwF4Dk%2BhcUUlt6Aob1JFFwYrKSynYBUcffyUxCbZX%2FvOXJQxSd%2BwU1ZuMcO34GVYAGgGtg3IzHnpgWIO30hzUap%2F4diTmpSomXx7bZwWj60zuDWBqdkuBf4ers%2Bzlf1UMOlDiRkRRLe7OaWH0dO9hOsAAAA&config=H4sIAAAAAAAAA12PSw6DMAxE95wCed1tNz1BNz2ElRoU5HxkBwmEuHsDJLRll3ljjydL07YwqIFHu%2BRnFhFFSU6dic4%2B4ZQJkHGoRmxMcKvuoJvVISvtaD0cSIJeuyAuu35krhilp7SH6b2kAIegVFMKoymReOQncSTRi%2Bust93829Kh7%2FkSkrEJLgqp%2FndsSk9w4T3uW%2BX3aY6b2tZc8IPCOWn1VWfPC98aR%2Fb6AXANGa1PAQAA

### SWC Info output

_No response_

### Expected behavior

keep name `set`

### Actual behavior

`set` is renamed to `set1` while usages are still `set`

```javascript
function set1(target, property, value, receiver) {
    if (typeof Reflect !== "undefined" && Reflect.set) {
        set1 = Reflect.set;
    } else {
        set1 = function set1(target, property, value, receiver) {
            var base = _super_prop_base(target, property);
            var desc;
            if (base) {
                desc = Object.getOwnPropertyDescriptor(base, property);
                if (desc.set) {
                    desc.set.call(receiver, value);
                    return true;
                } else if (!desc.writable) {
                    return false;
                }
            }
            desc = Object.getOwnPropertyDescriptor(receiver, property);
            if (desc) {
                if (!desc.writable) {
                    return false;
                }
                desc.value = value;
                Object.defineProperty(receiver, property, desc);
            } else {
                _define_property(receiver, property, value);
            }
            return true;
        };
    }
    return set1(target, property, value, receiver);
}
function _set(target, property, value, receiver, isStrict) {
    var s = set(target, property, value, receiver || target);
    if (!s && isStrict) {
        throw new Error("failed to set property");
    }
    return value;
}
```

### Version

1.7.36

### Additional context

_No response_


Discussion / hints:

Fix patch:
diff --git a/.changeset/sixty-ladybugs-help.md b/.changeset/sixty-ladybugs-help.md
new file mode 100644
index 000000000000..ecafeeb18b4b
--- /dev/null
+++ b/.changeset/sixty-ladybugs-help.md
@@ -0,0 +1,6 @@
+---
+swc_core: patch
+swc_ecma_transforms_base: patch
+---
+
+fix(es/renamer): check `preserved` in normal renaming mode
diff --git a/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs b/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs
index c613e7e047c9..07c9122c91cb 100644
--- a/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs
+++ b/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs
@@ -99,6 +99,7 @@ impl Scope {
         to: &mut RenameMap,
         previous: &RenameMap,
         reverse: &mut ReverseMap,
+        preserved: &FxHashSet<Id>,
         preserved_symbols: &FxHashSet<Atom>,
     ) where
         R: Renamer,
@@ -113,6 +114,7 @@ impl Scope {
             previous,
             reverse,
             queue,
+            preserved,
             preserved_symbols,
         );
 
@@ -122,6 +124,7 @@ impl Scope {
                 to,
                 &Default::default(),
                 reverse,
+                preserved,
                 preserved_symbols,
             );
         }
@@ -134,6 +137,7 @@ impl Scope {
         previous: &RenameMap,
         reverse: &mut ReverseMap,
         queue: Vec<Id>,
+        preserved: &FxHashSet<Id>,
         preserved_symbols: &FxHashSet<Atom>,
     ) where
         R: Renamer,
@@ -141,7 +145,11 @@ impl Scope {
         let mut n = 0;
 
         for id in queue {
-            if to.get(&id).is_some() || previous.get(&id).is_some() || id.0 == "eval" {
+            if preserved.contains(&id)
+                || to.get(&id).is_some()
+                || previous.get(&id).is_some()
+                || id.0 == "eval"
+            {
                 continue;
             }
 
diff --git a/crates/swc_ecma_transforms_base/src/rename/mod.rs b/crates/swc_ecma_transforms_base/src/rename/mod.rs
index 03aab3ac96fc..f427d94fb503 100644
--- a/crates/swc_ecma_transforms_base/src/rename/mod.rs
+++ b/crates/swc_ecma_transforms_base/src/rename/mod.rs
@@ -191,6 +191,7 @@ where
                 &mut map,
                 &self.previous_cache,
                 &mut Default::default(),
+                &self.preserved,
                 &unresolved,
             );
         }


Test patch:
diff --git a/crates/swc/tests/projects.rs b/crates/swc/tests/projects.rs
index 7a3b568c854d..eda7252199ac 100644
--- a/crates/swc/tests/projects.rs
+++ b/crates/swc/tests/projects.rs
@@ -601,6 +601,15 @@ fn issue_1203() {
     assert!(!f.contains("return //"))
 }
 
+#[test]
+fn issue_9663() {
+    let f = file("tests/projects/issue-9663/input.js").unwrap();
+    println!("{}", f);
+
+    assert!(f.contains("set = Reflect.set"));
+    assert!(!f.contains("function set1("));
+}
+
 #[test]
 fn codegen_1() {
     let f = file("tests/projects/codegen-1/input.js").unwrap();
diff --git a/crates/swc/tests/projects/issue-9663/.swcrc b/crates/swc/tests/projects/issue-9663/.swcrc
new file mode 100644
index 000000000000..744ceb040bb3
--- /dev/null
+++ b/crates/swc/tests/projects/issue-9663/.swcrc
@@ -0,0 +1,9 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript"
+        },
+        "externalHelpers": false,
+        "target": "es5"
+    }
+}
\ No newline at end of file
diff --git a/crates/swc/tests/projects/issue-9663/input.js b/crates/swc/tests/projects/issue-9663/input.js
new file mode 100644
index 000000000000..56968e30119e
--- /dev/null
+++ b/crates/swc/tests/projects/issue-9663/input.js
@@ -0,0 +1,15 @@
+class A {
+    world = false
+}
+
+eval("hello")
+
+class B extends A {
+    set hello(v) {
+        super.world = v
+    }
+
+    get hello() {
+        return super.world
+    }
+}
\ No newline at end of file