Repository: swc-project/swc (rust)

Issue:
privateMethod use keyword cause runtime error when translate to es5
### Describe the bug

when translate to es5, privateMethod use keyword such as '#switch' '#new' will translate to such 
```
 _class_private_method_get(this, _switch, _switch).call(this);
```
WeakSet and function name are both _switch ,will cause 
TypeError: _class_private_method_get(...).call is not a function

### Input code

```typescript
class TestCls{
    foo(){
        this.#bar()    
        this.#switch()    
    }
    #switch(){
        console.log("#switch called")
    }

    #bar(){
        console.log("#bar called")
    }
}
export {TestCls}

let a = new TestCls
a.foo()
```


### Config

```json
{
  "module": {
    "type": "commonjs" 
  },
  "jsc": { 
    "target": "es5", 
    "parser": {
      
      "syntax": "ecmascript",
      
      "jsx": false,
      
      "decorators": true,
      
      "dynamicImport": true,
      
      
    },

    "minify": {} 
  },
    
  
  "minify": false 
}
```


### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.3.100&code=H4sIAAAAAAAAA0vOSSwuVghJLS5xzimu5lIAgrT8fA1NCBMESjIyi%2FWUkxKLNDRBXDTx4vLMkuQMJKlaMAkXR5iTnJ9XnJ%2BTqpeTn66hBJVXSE7MyUlNUdKEaoXoBduFSyNQEl1XLVdqRUF%2BUYlCNdQfQINyUksUEhVsFfJSy2G%2B40rUA3sNAKLByCDzAAAA&config=H4sIAAAAAAAAA1WPSw7DIAwF9zkF8rrbdtE79BAWdSIifrKJVBTl7iUE0maH3xsz8jooBbNoeKq1PMsQkYX4nEsi2Sf8lARIOxTNJia49XaWvRrRCtVoOxpIyBOluiX3hoMNQajjLXPGmzH%2FC3VwkUnkCu4o%2BsnSVTc0JbjwXmrZDkk50qF%2FwA%2FqsvNjMPLqm4kXGrYvhlQioBQBAAA%3D

### SWC Info output

_No response_

### Expected behavior

no runtime error

### Actual behavior

``` javascript
var _switch = new WeakSet(), _bar = new WeakSet();
var TestCls = function() {
    "use strict";
    function TestCls() {
        _class_call_check(this, TestCls);
        _class_private_method_init(this, _switch);
        _class_private_method_init(this, _bar);
    }
    _create_class(TestCls, [
        {
            key: "foo",
            value: function foo() {
                _class_private_method_get(this, _bar, bar).call(this);
                _class_private_method_get(this, _switch, _switch).call(this);
            }
        }
    ]);
    return TestCls;
}();
function _switch() {
    console.log("#switch called");
}
function bar() {
    console.log("#bar called");
}
var a = new TestCls;
a.foo();
```

### Version

1.5.5

### Additional context

_No response_

Pull request:
fix(es/compat): Avoid reserved name for private method
**Description:**

Following development should follow babel/babel#16261 to avoid separate `WeakMap` for each individual private method


**Related issue:**

 - Closes #8948.

Patch:
diff --git a/crates/swc_ecma_ast/src/ident.rs b/crates/swc_ecma_ast/src/ident.rs
index e5bded9fa0..97ed0ed65f 100644
--- a/crates/swc_ecma_ast/src/ident.rs
+++ b/crates/swc_ecma_ast/src/ident.rs
@@ -425,6 +425,8 @@ static RESSERVED_IN_STRICT_MODE: phf::Set<&str> = phf_set!(
     "yield",
 );
 
+static RESSERVED_IN_STRICT_BIND: phf::Set<&str> = phf_set!("eval", "arguments",);
+
 static RESERVED_IN_ES3: phf::Set<&str> = phf_set!(
     "abstract",
     "boolean",
@@ -457,12 +459,19 @@ pub trait IdentExt: AsRef<str> {
     }
 
     fn is_reserved_in_strict_bind(&self) -> bool {
-        ["eval", "arguments"].contains(&self.as_ref())
+        RESSERVED_IN_STRICT_BIND.contains(self.as_ref())
     }
 
     fn is_reserved_in_es3(&self) -> bool {
         RESERVED_IN_ES3.contains(self.as_ref())
     }
+
+    fn is_reserved_in_any(&self) -> bool {
+        RESERVED.contains(self.as_ref())
+            || RESSERVED_IN_STRICT_MODE.contains(self.as_ref())
+            || RESSERVED_IN_STRICT_BIND.contains(self.as_ref())
+            || RESERVED_IN_ES3.contains(self.as_ref())
+    }
 }
 
 impl IdentExt for Atom {}
diff --git a/crates/swc_ecma_compat_es2022/src/class_properties/mod.rs b/crates/swc_ecma_compat_es2022/src/class_properties/mod.rs
index 054745604a..f386302ba7 100644
--- a/crates/swc_ecma_compat_es2022/src/class_properties/mod.rs
+++ b/crates/swc_ecma_compat_es2022/src/class_properties/mod.rs
@@ -1,9 +1,6 @@
 use swc_common::{
-    collections::{AHashMap, AHashSet},
-    comments::Comments,
-    errors::HANDLER,
-    util::take::Take,
-    Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
+    collections::AHashMap, comments::Comments, errors::HANDLER, util::take::Take, Mark, Span,
+    Spanned, SyntaxContext, DUMMY_SP,
 };
 use swc_ecma_ast::*;
 use swc_ecma_transforms_base::{helper, perf::Check};
@@ -530,7 +527,6 @@ impl<C: Comments> ClassProperties<C> {
         let mut super_ident = None;
 
         class.body.visit_mut_with(&mut BrandCheckHandler {
-            names: &mut AHashSet::default(),
             private: &self.private,
         });
 
@@ -806,7 +802,13 @@ impl<C: Comments> ClassProperties<C> {
                         match method.kind {
                             MethodKind::Getter => format!("get_{}", method.key.id.sym).into(),
                             MethodKind::Setter => format!("set_{}", method.key.id.sym).into(),
-                            MethodKind::Method => method.key.id.sym.clone(),
+                            MethodKind::Method => {
+                                if method.key.id.is_reserved_in_any() {
+                                    format!("__{}", method.key.id.sym).into()
+                                } else {
+                                    method.key.id.sym.clone()
+                                }
+                            }
                         },
                         method
                             .span
diff --git a/crates/swc_ecma_compat_es2022/src/class_properties/private_field.rs b/crates/swc_ecma_compat_es2022/src/class_properties/private_field.rs
index c72e31ca04..2f96a111ae 100644
--- a/crates/swc_ecma_compat_es2022/src/class_properties/private_field.rs
+++ b/crates/swc_ecma_compat_es2022/src/class_properties/private_field.rs
@@ -2,10 +2,8 @@ use std::iter;
 
 use swc_atoms::JsWord;
 use swc_common::{
-    collections::{AHashMap, AHashSet},
-    errors::HANDLER,
-    util::take::Take,
-    Mark, Spanned, SyntaxContext, DUMMY_SP,
+    collections::AHashMap, errors::HANDLER, util::take::Take, Mark, Spanned, SyntaxContext,
+    DUMMY_SP,
 };
 use swc_ecma_ast::*;
 use swc_ecma_transforms_base::helper;
@@ -83,9 +81,6 @@ impl PrivateKind {
 }
 
 pub(super) struct BrandCheckHandler<'a> {
-    /// Private names used for brand checks.
-    pub names: &'a mut AHashSet<JsWord>,
-
     pub private: &'a PrivateRecord,
 }
 
@@ -117,8 +112,6 @@ impl VisitMut for BrandCheckHandler<'_> {
                     }
                 }
 
-                self.names.insert(n.id.sym.clone());
-
                 let (mark, kind, class_name) = self.private.get(&n.id);
 
                 if mark == Mark::root() {
@@ -596,7 +589,11 @@ impl<'a> PrivateAccessVisitor<'a> {
         }
 
         let method_name = Ident::new(
-            n.id.sym.clone(),
+            if n.id.is_reserved_in_any() {
+                format!("__{}", n.id.sym).into()
+            } else {
+                n.id.sym.clone()
+            },
             n.id.span.with_ctxt(SyntaxContext::empty()).apply_mark(mark),
         );
         let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));


Test patch:
diff --git a/crates/swc_ecma_transforms_compat/tests/__swc_snapshots__/tests/es2022_class_properties.rs/loose_keyword_method.js b/crates/swc_ecma_transforms_compat/tests/__swc_snapshots__/tests/es2022_class_properties.rs/loose_keyword_method.js
new file mode 100644
index 0000000000..2dc61406e4
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/tests/__swc_snapshots__/tests/es2022_class_properties.rs/loose_keyword_method.js
@@ -0,0 +1,24 @@
+var _switch = /*#__PURE__*/ _class_private_field_loose_key("_switch"), _bar = /*#__PURE__*/ _class_private_field_loose_key("_bar");
+class TestCls {
+    foo() {
+        _class_private_field_loose_base(this, _bar)[_bar]();
+        _class_private_field_loose_base(this, _switch)[_switch]();
+    }
+    constructor(){
+        Object.defineProperty(this, _switch, {
+            value: __switch
+        });
+        Object.defineProperty(this, _bar, {
+            value: bar
+        });
+    }
+}
+function __switch() {
+    console.log("#switch called");
+}
+function bar() {
+    console.log("#bar called");
+}
+export { TestCls };
+let a = new TestCls;
+a.foo();
diff --git a/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/input.js b/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/input.js
new file mode 100644
index 0000000000..1ac174f9e0
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/input.js
@@ -0,0 +1,16 @@
+class TestCls {
+    foo() {
+        this.#bar();
+        this.#switch();
+    }
+    #switch() {
+        console.log("#switch called");
+    }
+
+    #bar() {
+        console.log("#bar called");
+    }
+}
+
+let a = new TestCls();
+a.foo();
diff --git a/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/output.js b/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/output.js
new file mode 100644
index 0000000000..787b183038
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/tests/class-properties/issue-8948/output.js
@@ -0,0 +1,19 @@
+var _switch = /*#__PURE__*/ new WeakSet(), _bar = /*#__PURE__*/ new WeakSet();
+class TestCls {
+    foo() {
+        _class_private_method_get(this, _bar, bar).call(this);
+        _class_private_method_get(this, _switch, __switch).call(this);
+    }
+    constructor(){
+        _class_private_method_init(this, _switch);
+        _class_private_method_init(this, _bar);
+    }
+}
+function __switch() {
+    console.log("#switch called");
+}
+function bar() {
+    console.log("#bar called");
+}
+let a = new TestCls();
+a.foo();
diff --git a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
index aa0f9ff31e..5ceb869d1f 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
@@ -4831,6 +4831,46 @@ class Cl {
 "#
 );
 
+test!(
+    syntax(),
+    |t| {
+        let unresolved_mark = Mark::new();
+        let top_level_mark = Mark::new();
+
+        chain!(
+            resolver(unresolved_mark, top_level_mark, false),
+            class_properties(
+                Some(t.comments.clone()),
+                class_properties::Config {
+                    private_as_properties: true,
+                    ..Default::default()
+                },
+                unresolved_mark,
+            )
+        )
+    },
+    loose_keyword_method,
+    r##"
+class TestCls{
+    foo(){
+        this.#bar()    
+        this.#switch()    
+    }
+    #switch(){
+        console.log("#switch called")
+    }
+
+    #bar(){
+        console.log("#bar called")
+    }
+}
+export {TestCls}
+
+let a = new TestCls
+a.foo()
+"##
+);
+
 #[testing::fixture("tests/classes/**/exec.js")]
 fn exec(input: PathBuf) {
     let src = read_to_string(input).unwrap();