swc_ecma_compat_es2015/classes/
mod.rs

1use std::iter;
2
3use rustc_hash::FxBuildHasher;
4use serde::Deserialize;
5use swc_atoms::atom;
6use swc_common::{util::take::Take, BytePos, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::{helper, native::is_native, perf::Check};
9use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
10use swc_ecma_transforms_macros::fast_path;
11use swc_ecma_utils::{
12    alias_if_required, contains_this_expr, is_valid_ident, is_valid_prop_ident, prepend_stmt,
13    private_ident, prop_name_to_expr, quote_expr, quote_ident, quote_str, replace_ident,
14    ExprFactory, ModuleItemLike, StmtLike,
15};
16use swc_ecma_visit::{
17    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
18};
19use swc_trace_macro::swc_trace;
20
21use self::{
22    constructor::fold_constructor,
23    prop_name::{is_pure_prop_name, should_extract_class_prop_key, HashKey},
24};
25
26mod constructor;
27mod prop_name;
28
29pub fn classes(config: Config) -> impl Pass {
30    visit_mut_pass(Classes {
31        in_strict: false,
32        config,
33
34        params: Default::default(),
35        args: Default::default(),
36    })
37}
38
39type IndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
40
41/// `@babel/plugin-transform-classes`
42///
43/// # In
44/// ```js
45/// class Test {
46///   constructor(name) {
47///     this.name = name;
48///   }
49///
50///   logger () {
51///     console.log("Hello", this.name);
52///   }
53/// }
54/// ```
55///
56/// # Out
57/// ```js
58/// var Test = function () {
59///   function Test(name) {
60///     _class_call_check(this, Test);
61///
62///     this.name = name;
63///   }
64///
65///   Test.prototype.logger = function logger() {
66///     console.log("Hello", this.name);
67///   };
68///
69///   return Test;
70/// }();
71/// ```
72#[derive(Default, Clone)]
73struct Classes {
74    in_strict: bool,
75    config: Config,
76
77    params: Vec<Param>,
78    args: Vec<ExprOrSpread>,
79}
80
81#[derive(Debug, Clone, Copy, Default, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Config {
84    #[serde(default)]
85    pub constant_super: bool,
86    #[serde(default)]
87    pub no_class_calls: bool,
88    #[serde(default)]
89    pub set_class_methods: bool,
90    #[serde(default)]
91    pub super_is_callable_constructor: bool,
92}
93
94struct Data {
95    key_prop: Box<PropName>,
96    method: Option<Box<Expr>>,
97    set: Option<Box<Expr>>,
98    get: Option<Box<Expr>>,
99}
100
101#[swc_trace]
102impl Classes {
103    fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
104    where
105        T: StmtLike + ModuleItemLike + VisitMutWith<Self> + Take,
106    {
107        let mut buf = Vec::with_capacity(stmts.len());
108        let mut first = true;
109        let old = self.in_strict;
110
111        for stmt in stmts.iter_mut() {
112            match T::try_into_stmt(stmt.take()) {
113                Err(node) => match node.try_into_module_decl() {
114                    Ok(mut decl) => {
115                        match decl {
116                            ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
117                                decl: DefaultDecl::Class(ClassExpr { ident, class }),
118                                ..
119                            }) => {
120                                let ident =
121                                    ident.unwrap_or_else(|| quote_ident!("_default").into());
122
123                                let mut decl = self.fold_class_as_var_decl(ident.clone(), class);
124                                decl.visit_mut_children_with(self);
125                                buf.push(T::from(decl.into()));
126
127                                buf.push(
128                                    match T::try_from_module_decl(
129                                        NamedExport {
130                                            span: DUMMY_SP,
131                                            specifiers: vec![ExportNamedSpecifier {
132                                                span: DUMMY_SP,
133                                                orig: ModuleExportName::Ident(ident),
134                                                exported: Some(ModuleExportName::Ident(
135                                                    quote_ident!("default").into(),
136                                                )),
137                                                is_type_only: false,
138                                            }
139                                            .into()],
140                                            src: None,
141                                            type_only: false,
142                                            with: None,
143                                        }
144                                        .into(),
145                                    ) {
146                                        Ok(t) => t,
147                                        Err(..) => unreachable!(),
148                                    },
149                                );
150                            }
151                            ModuleDecl::ExportDecl(ExportDecl {
152                                span,
153                                decl:
154                                    Decl::Class(ClassDecl {
155                                        ident,
156                                        declare: false,
157                                        class,
158                                    }),
159                                ..
160                            }) => {
161                                let mut decl = self.fold_class_as_var_decl(ident, class);
162                                decl.visit_mut_children_with(self);
163                                buf.push(
164                                    match T::try_from_module_decl(
165                                        ExportDecl {
166                                            span,
167                                            decl: decl.into(),
168                                        }
169                                        .into(),
170                                    ) {
171                                        Ok(t) => t,
172                                        Err(..) => unreachable!(),
173                                    },
174                                );
175                            }
176                            _ => buf.push({
177                                decl.visit_mut_children_with(self);
178                                match T::try_from_module_decl(decl) {
179                                    Ok(t) => t,
180                                    Err(..) => unreachable!(),
181                                }
182                            }),
183                        };
184                    }
185                    Err(..) => unreachable!(),
186                },
187                Ok(mut stmt) => {
188                    if first {
189                        self.in_strict |= stmt.is_use_strict();
190                    }
191
192                    stmt.visit_mut_children_with(self);
193                    buf.push(T::from(stmt));
194                }
195            }
196            first = false;
197        }
198
199        self.in_strict = old;
200        *stmts = buf;
201    }
202}
203
204#[swc_trace]
205#[fast_path(ClassFinder)]
206impl VisitMut for Classes {
207    noop_visit_mut_type!(fail);
208
209    fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
210        self.visit_mut_stmt_like(items)
211    }
212
213    fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {
214        self.visit_mut_stmt_like(items)
215    }
216
217    fn visit_mut_decl(&mut self, n: &mut Decl) {
218        if let Decl::Class(decl) = n {
219            *n = self
220                .fold_class_as_var_decl(decl.ident.take(), decl.class.take())
221                .into()
222        };
223
224        n.visit_mut_children_with(self);
225    }
226
227    fn visit_mut_expr(&mut self, n: &mut Expr) {
228        match n {
229            Expr::Class(e) => {
230                let mut class = self.fold_class(e.ident.take(), e.class.take());
231                if let Expr::Call(call) = &mut class {
232                    self.add_pure_comments(&mut call.span.lo)
233                }
234                *n = class;
235                n.visit_mut_children_with(self)
236            }
237
238            _ => n.visit_mut_children_with(self),
239        }
240    }
241
242    fn visit_mut_var_declarator(&mut self, d: &mut VarDeclarator) {
243        // will there be anything else in var name at this point?
244        if let VarDeclarator {
245            name: Pat::Ident(i),
246            init: Some(init),
247            ..
248        } = d
249        {
250            if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **init {
251                c.ident = Some(Ident::from(&*i).into_private())
252            }
253        }
254
255        d.visit_mut_children_with(self)
256    }
257
258    /// `let { f = class /* f */ {} } = {};`
259    fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
260        if let Some(value) = &mut n.value {
261            if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **value {
262                c.ident = Some(Ident::from(&n.key).into_private());
263            }
264        }
265
266        n.visit_mut_children_with(self);
267    }
268
269    /// `let [c = class /* c */ {}] = [];`
270    /// `function foo(bar = class /* bar */ {}) {}`
271    fn visit_mut_assign_pat(&mut self, n: &mut AssignPat) {
272        if let (Pat::Ident(id), Expr::Class(c @ ClassExpr { ident: None, .. })) =
273            (&*n.left, &mut *n.right)
274        {
275            c.ident = Some(Ident::from(id).into_private());
276        }
277
278        n.visit_mut_children_with(self);
279    }
280
281    /// {
282    ///     hello: class {},
283    ///     "foo": class {},
284    ///     ["x"]: class {}
285    /// }
286    fn visit_mut_key_value_prop(&mut self, n: &mut KeyValueProp) {
287        if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut *n.value {
288            match &n.key {
289                PropName::Ident(ident) => {
290                    c.ident = Some(Ident::from(ident.clone()).into_private());
291                }
292                PropName::Str(Str { value, span, .. }) => {
293                    if is_valid_prop_ident(value) {
294                        c.ident = Some(private_ident!(*span, value.clone()));
295                    }
296                }
297                PropName::Computed(ComputedPropName { expr, .. }) => {
298                    if let Expr::Lit(Lit::Str(Str { value, span, .. })) = &**expr {
299                        if is_valid_prop_ident(value) {
300                            c.ident = Some(private_ident!(*span, value.clone()));
301                        }
302                    }
303                }
304                _ => {}
305            }
306        }
307
308        n.visit_mut_children_with(self)
309    }
310
311    fn visit_mut_assign_expr(&mut self, a: &mut AssignExpr) {
312        if let AssignExpr {
313            op: op!("=") | op!("||=") | op!("??="),
314            left,
315            right,
316            ..
317        } = a
318        {
319            if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **right {
320                if let AssignTarget::Simple(SimpleAssignTarget::Ident(ident)) = left {
321                    c.ident = Some(Ident::from(&*ident).into_private())
322                }
323            }
324        }
325
326        a.visit_mut_children_with(self)
327    }
328}
329
330#[swc_trace]
331impl Classes {
332    fn add_pure_comments(&mut self, start: &mut BytePos) {
333        *start = BytePos::PURE;
334    }
335
336    fn fold_class_as_var_decl(&mut self, ident: Ident, class: Box<Class>) -> VarDecl {
337        let span = class.span;
338        let mut rhs = self.fold_class(Some(ident.clone()), class);
339
340        let mut new_name = ident.clone();
341        new_name.ctxt = new_name.ctxt.apply_mark(Mark::new());
342
343        replace_ident(&mut rhs, ident.to_id(), &new_name);
344
345        // let VarDecl take every comments except pure
346        if let Expr::Call(call) = &mut rhs {
347            let mut span = Span {
348                // after class
349                lo: span.lo + BytePos(5),
350                ..span
351            };
352            self.add_pure_comments(&mut span.lo);
353            call.span = span;
354        }
355
356        VarDecl {
357            span,
358            kind: VarDeclKind::Let,
359            decls: vec![VarDeclarator {
360                span,
361                init: Some(Box::new(rhs)),
362                // Foo in var Foo =
363                name: ident.into(),
364                definite: false,
365            }],
366            ..Default::default()
367        }
368    }
369
370    /// Turns class expression into iife.
371    ///
372    /// ```js
373    /// class Foo {}
374    /// ```
375    ///
376    /// ```js
377    /// function() {
378    ///   var Foo = function Foo(){
379    ///   };
380    /// }()
381    /// ```
382    fn fold_class(&mut self, class_name: Option<Ident>, class: Box<Class>) -> Expr {
383        let span = class.span;
384
385        // Ident of the super class *inside* function.
386        let super_ident = class
387            .super_class
388            .as_ref()
389            .map(|e| alias_if_required(e, "_superClass").0);
390        let has_super = super_ident.is_some();
391        let (mut params, mut args, super_ident) = if let Some(ref super_ident) = super_ident {
392            // Param should have a separate syntax context from arg.
393            let super_param = private_ident!(super_ident.sym.clone());
394            let params = vec![Param {
395                span: DUMMY_SP,
396                decorators: Default::default(),
397                pat: super_param.clone().into(),
398            }];
399
400            let super_class = class.super_class.clone().unwrap();
401            let is_super_native = match *super_class {
402                Expr::Ident(Ident { ref sym, .. }) => is_native(sym),
403                _ => false,
404            };
405            if is_super_native {
406                (
407                    params,
408                    vec![CallExpr {
409                        span: DUMMY_SP,
410                        callee: helper!(wrap_native_super),
411                        args: vec![super_class.as_arg()],
412                        ..Default::default()
413                    }
414                    .as_arg()],
415                    Some(super_param),
416                )
417            } else {
418                (params, vec![super_class.as_arg()], Some(super_param))
419            }
420        } else {
421            (Vec::new(), Vec::new(), None)
422        };
423
424        let mut stmts = self.class_to_stmts(class_name, super_ident, class);
425        params.extend(self.params.take());
426        args.extend(self.args.take());
427
428        let cnt_of_non_directive = stmts
429            .iter()
430            .filter(|stmt| match stmt {
431                Stmt::Expr(ExprStmt { expr, .. }) => !matches!(&**expr, Expr::Lit(Lit::Str(..))),
432                _ => true,
433            })
434            .count();
435        if !has_super && cnt_of_non_directive == 1 {
436            //    class Foo {}
437            //
438            // should be
439            //
440            //    var Foo = function Foo() {
441            //        _class_call_check(this, Foo);
442            //    };
443            //
444            // instead of
445            //
446            //    var Foo = function(){
447            //      function Foo() {
448            //          _class_call_check(this, Foo);
449            //      }
450            //
451            //      return Foo;
452            //    }();
453
454            let stmt = stmts.pop().unwrap();
455            match stmt {
456                Stmt::Decl(Decl::Fn(FnDecl {
457                    ident,
458                    mut function,
459                    ..
460                })) => {
461                    if let Some(use_strict) = stmts.pop() {
462                        prepend_stmt(&mut function.body.as_mut().unwrap().stmts, use_strict);
463                    }
464                    function.span = span;
465                    return FnExpr {
466                        ident: Some(ident),
467                        function,
468                    }
469                    .into();
470                }
471                _ => unreachable!(),
472            }
473        }
474
475        let body = BlockStmt {
476            span: DUMMY_SP,
477            stmts,
478            ..Default::default()
479        };
480
481        let call = CallExpr {
482            span,
483            callee: Function {
484                span,
485                is_async: false,
486                is_generator: false,
487                params,
488                body: Some(body),
489                ..Default::default()
490            }
491            .as_callee(),
492            args,
493            ..Default::default()
494        };
495
496        call.into()
497    }
498
499    /// Returned `stmts` contains `return Foo`
500    fn class_to_stmts(
501        &mut self,
502        class_name: Option<Ident>,
503        super_class_ident: Option<Ident>,
504        class: Box<Class>,
505    ) -> Vec<Stmt> {
506        let class_name = class_name.unwrap_or_else(|| quote_ident!("_class").into());
507        let mut stmts = Vec::new();
508
509        let mut methods = Vec::new();
510        let mut constructor = None;
511        for member in class.body {
512            match member {
513                ClassMember::Constructor(c) => constructor = Some(c),
514                ClassMember::Method(m) => methods.push(m),
515
516                ClassMember::PrivateMethod(_)
517                | ClassMember::ClassProp(..)
518                | ClassMember::PrivateProp(..)
519                | ClassMember::TsIndexSignature(..)
520                | ClassMember::StaticBlock(..)
521                | ClassMember::AutoAccessor(..) => {}
522                ClassMember::Empty(..) => {}
523            }
524        }
525
526        if let Some(ref super_class_ident) = super_class_ident {
527            // inject helper methods
528
529            let mut class_name_sym = class_name.clone();
530            class_name_sym.span = DUMMY_SP;
531            class_name_sym.ctxt = class_name.ctxt;
532
533            let mut super_class_name_sym = super_class_ident.clone();
534            super_class_name_sym.span = DUMMY_SP;
535            super_class_name_sym.ctxt = super_class_ident.ctxt;
536
537            stmts.push(
538                CallExpr {
539                    span: DUMMY_SP,
540                    callee: helper!(inherits),
541                    args: vec![class_name_sym.as_arg(), super_class_name_sym.as_arg()],
542                    ..Default::default()
543                }
544                .into_stmt(),
545            );
546        }
547
548        // constructor
549        stmts.push(
550            fold_constructor(
551                class.span,
552                constructor,
553                &class_name,
554                &super_class_ident,
555                self.config,
556            )
557            .into(),
558        );
559
560        // convert class methods
561        stmts.extend(self.fold_class_methods(&class_name, &super_class_ident, methods));
562
563        if stmts.first().map(|v| !v.is_use_strict()).unwrap_or(false) && !self.in_strict {
564            prepend_stmt(
565                &mut stmts,
566                Lit::Str(Str {
567                    span: DUMMY_SP,
568                    value: atom!("use strict"),
569                    raw: Some(atom!("\"use strict\"")),
570                })
571                .into_stmt(),
572            );
573
574            if stmts.len() == 2 {
575                return stmts;
576            }
577        }
578
579        if super_class_ident.is_none()
580            && stmts
581                .iter()
582                .filter(|stmt| match stmt {
583                    Stmt::Expr(ExprStmt { expr, .. }) => {
584                        !matches!(&**expr, Expr::Lit(Lit::Str(..)))
585                    }
586                    _ => true,
587                })
588                .count()
589                == 1
590        {
591            return stmts;
592        }
593
594        let mut class_name_sym = class_name.clone();
595        class_name_sym.span = DUMMY_SP;
596        class_name_sym.ctxt = class_name.ctxt;
597
598        // `return Foo`
599        stmts.push(
600            ReturnStmt {
601                span: DUMMY_SP,
602                arg: Some(class_name_sym.into()),
603            }
604            .into(),
605        );
606
607        stmts
608    }
609
610    fn fold_class_methods(
611        &mut self,
612        class_name: &Ident,
613        super_class_ident: &Option<Ident>,
614        methods: Vec<ClassMethod>,
615    ) -> Vec<Stmt> {
616        if methods.is_empty() {
617            return Vec::new();
618        }
619
620        /// { key: "prop" }
621        fn mk_key_prop(key: PropName) -> Box<Prop> {
622            Box::new(Prop::KeyValue(KeyValueProp {
623                key: PropName::Ident(quote_ident!(Default::default(), key.span(), "key").into()),
624                value: match key {
625                    PropName::Ident(i) => Lit::Str(quote_str!(i.span, i.sym)).into(),
626                    PropName::Str(s) => s.into(),
627                    PropName::Num(n) => n.into(),
628                    PropName::BigInt(b) => Str {
629                        span: b.span,
630                        raw: None,
631                        value: b.value.to_string().into(),
632                    }
633                    .into(),
634                    PropName::Computed(c) => c.expr,
635                },
636            }))
637        }
638
639        fn mk_key_prop_member(key: PropName) -> MemberProp {
640            match key {
641                PropName::Ident(i) => MemberProp::Ident(i),
642                PropName::Str(s) => MemberProp::Computed(ComputedPropName {
643                    span: s.span,
644                    expr: Lit::Str(s).into(),
645                }),
646                PropName::Num(n) => MemberProp::Computed(ComputedPropName {
647                    span: n.span,
648                    expr: Lit::Num(n).into(),
649                }),
650                PropName::BigInt(b) => MemberProp::Computed(ComputedPropName {
651                    span: b.span,
652                    expr: Str {
653                        span: b.span,
654                        raw: None,
655                        value: b.value.to_string().into(),
656                    }
657                    .into(),
658                }),
659                PropName::Computed(c) => MemberProp::Computed(c),
660            }
661        }
662
663        fn mk_arg_obj_for_create_class(props: IndexMap<HashKey, Data>) -> ExprOrSpread {
664            if props.is_empty() {
665                return quote_expr!(DUMMY_SP, null).as_arg();
666            }
667            ArrayLit {
668                span: DUMMY_SP,
669                elems: props
670                    .into_iter()
671                    .map(|(_, data)| {
672                        let mut props = vec![PropOrSpread::Prop(mk_key_prop(*data.key_prop))];
673
674                        macro_rules! add {
675                            ($field:expr, $kind:expr, $s:literal) => {{
676                                if let Some(value) = $field {
677                                    let value = escape_keywords(value);
678                                    props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(
679                                        KeyValueProp {
680                                            key: PropName::Ident(quote_ident!($s)),
681                                            value,
682                                        },
683                                    ))));
684                                }
685                            }};
686                        }
687
688                        add!(data.get, MethodKind::Getter, "get");
689                        add!(data.set, MethodKind::Setter, "set");
690                        add!(data.method, MethodKind::Method, "value");
691
692                        ObjectLit {
693                            span: DUMMY_SP,
694                            props,
695                        }
696                        .as_arg()
697                    })
698                    .map(Some)
699                    .collect(),
700            }
701            .as_arg()
702        }
703
704        /// _create_class(Foo, [{}], [{}]);
705        fn mk_create_class_call(
706            class_name: Ident,
707            methods: ExprOrSpread,
708            static_methods: Option<ExprOrSpread>,
709        ) -> Stmt {
710            let mut class_name_sym = class_name.clone();
711            class_name_sym.span = DUMMY_SP;
712            class_name_sym.ctxt = class_name.ctxt;
713
714            CallExpr {
715                span: DUMMY_SP,
716                callee: helper!(create_class),
717                args: iter::once(class_name_sym.as_arg())
718                    .chain(iter::once(methods))
719                    .chain(static_methods)
720                    .collect(),
721                ..Default::default()
722            }
723            .into_stmt()
724        }
725
726        let (mut props, mut static_props) = (IndexMap::default(), IndexMap::default());
727
728        let should_extract = should_extract_class_prop_key(&methods);
729
730        for mut m in methods {
731            let key = HashKey::from(&m.key);
732            let key_is_pure = is_pure_prop_name(&m.key);
733            // class is always strict, however computed key is not part of class
734            let key_contain_this = !self.in_strict && contains_this_expr(&m.key);
735            let key_prop = Box::new(m.key.clone());
736            let computed = matches!(m.key, PropName::Computed(..));
737            let prop_name = prop_name_to_expr(m.key);
738
739            let key_prop = if should_extract && !key_is_pure || key_contain_this {
740                let ident = private_ident!("_prop");
741
742                self.params.push(ident.clone().into());
743                self.args.push(prop_name.clone().into());
744
745                Box::new(PropName::Computed(ComputedPropName {
746                    span: DUMMY_SP,
747                    expr: Box::new(ident.into()),
748                }))
749            } else {
750                key_prop
751            };
752
753            let append_to: &mut IndexMap<_, _> = if m.is_static {
754                &mut static_props
755            } else {
756                &mut props
757            };
758
759            let mut folder = SuperFieldAccessFolder {
760                class_name,
761
762                constructor_this_mark: None,
763                is_static: m.is_static,
764                folding_constructor: false,
765                in_nested_scope: false,
766                in_injected_define_property_call: false,
767                this_alias_mark: None,
768                constant_super: self.config.constant_super,
769                super_class: super_class_ident,
770                in_pat: false,
771            };
772            m.function.visit_mut_with(&mut folder);
773
774            if let Some(mark) = folder.this_alias_mark {
775                prepend_stmt(
776                    &mut m.function.body.as_mut().unwrap().stmts,
777                    VarDecl {
778                        span: DUMMY_SP,
779                        declare: false,
780                        kind: VarDeclKind::Var,
781                        decls: vec![VarDeclarator {
782                            span: DUMMY_SP,
783                            name: quote_ident!(SyntaxContext::empty().apply_mark(mark), "_this")
784                                .into(),
785                            init: Some(Box::new(Expr::This(ThisExpr { span: DUMMY_SP }))),
786                            definite: false,
787                        }],
788                        ..Default::default()
789                    }
790                    .into(),
791                );
792            }
793
794            let value = FnExpr {
795                ident: if m.kind == MethodKind::Method && !computed {
796                    match prop_name {
797                        Expr::Ident(ident) => Some(private_ident!(ident.span, ident.sym)),
798                        Expr::Lit(Lit::Str(Str { span, value, .. })) if is_valid_ident(&value) => {
799                            Some(Ident::new(
800                                value,
801                                span,
802                                SyntaxContext::empty().apply_mark(Mark::new()),
803                            ))
804                        }
805                        _ => None,
806                    }
807                } else {
808                    None
809                },
810                function: m.function,
811            }
812            .into();
813
814            let data = append_to.entry(key).or_insert_with(|| Data {
815                key_prop,
816                get: None,
817                set: None,
818                method: None,
819            });
820            match m.kind {
821                // https://github.com/swc-project/swc/issues/5029
822                MethodKind::Getter => {
823                    data.method = None;
824                    data.get = Some(value)
825                }
826                MethodKind::Setter => {
827                    data.method = None;
828                    data.set = Some(value)
829                }
830                MethodKind::Method => {
831                    data.get = None;
832                    data.set = None;
833                    data.method = Some(value)
834                }
835            }
836        }
837
838        let mut res = Vec::new();
839
840        if self.config.set_class_methods {
841            let proto = private_ident!("_proto");
842            props.retain(|_, v| {
843                if let Some(method) = v.method.take() {
844                    if res.is_empty() {
845                        res.push(
846                            VarDecl {
847                                span: DUMMY_SP,
848                                kind: VarDeclKind::Var,
849                                declare: false,
850                                decls: vec![VarDeclarator {
851                                    span: DUMMY_SP,
852                                    name: proto.clone().into(),
853                                    init: Some(
854                                        class_name
855                                            .clone()
856                                            .make_member(quote_ident!("prototype"))
857                                            .into(),
858                                    ),
859                                    definite: false,
860                                }],
861                                ..Default::default()
862                            }
863                            .into(),
864                        );
865                    }
866                    let span = method.span();
867                    let prop = *v.key_prop.clone();
868                    res.push(
869                        ExprStmt {
870                            span,
871                            expr: AssignExpr {
872                                span,
873                                op: op!("="),
874                                left: MemberExpr {
875                                    span,
876                                    obj: Box::new(proto.clone().into()),
877                                    prop: mk_key_prop_member(prop),
878                                }
879                                .into(),
880                                right: escape_keywords(method),
881                            }
882                            .into(),
883                        }
884                        .into(),
885                    );
886                    !(v.get.is_none() && v.set.is_none())
887                } else {
888                    true
889                }
890            });
891
892            static_props.retain(|_, v| {
893                if let Some(method) = v.method.take() {
894                    let span = method.span();
895                    let prop = *v.key_prop.clone();
896                    res.push(
897                        ExprStmt {
898                            span,
899                            expr: AssignExpr {
900                                span,
901                                op: op!("="),
902                                left: MemberExpr {
903                                    span,
904                                    obj: Box::new(class_name.clone().into()),
905                                    prop: mk_key_prop_member(prop),
906                                }
907                                .into(),
908                                right: escape_keywords(method),
909                            }
910                            .into(),
911                        }
912                        .into(),
913                    );
914                    !(v.get.is_none() && v.set.is_none())
915                } else {
916                    true
917                }
918            })
919        }
920
921        if props.is_empty() && static_props.is_empty() {
922            return res;
923        }
924
925        res.push(mk_create_class_call(
926            class_name.clone(),
927            mk_arg_obj_for_create_class(props),
928            if static_props.is_empty() {
929                None
930            } else {
931                Some(mk_arg_obj_for_create_class(static_props))
932            },
933        ));
934
935        res
936    }
937}
938
939#[tracing::instrument(level = "debug", skip_all)]
940fn inject_class_call_check(c: &mut Vec<Stmt>, name: Ident) {
941    let mut class_name_sym = name.clone();
942    class_name_sym.span = DUMMY_SP;
943    class_name_sym.ctxt = name.ctxt;
944
945    let class_call_check = CallExpr {
946        span: DUMMY_SP,
947        callee: helper!(class_call_check),
948        args: vec![
949            Expr::This(ThisExpr { span: DUMMY_SP }).as_arg(),
950            class_name_sym.as_arg(),
951        ],
952        ..Default::default()
953    }
954    .into_stmt();
955
956    prepend_stmt(c, class_call_check)
957}
958
959/// Returns true if no `super` is used before `super()` call.
960#[tracing::instrument(level = "debug", skip_all)]
961fn is_always_initialized(body: &[Stmt]) -> bool {
962    struct SuperFinder {
963        found: bool,
964    }
965
966    impl Visit for SuperFinder {
967        noop_visit_type!(fail);
968
969        fn visit_callee(&mut self, node: &Callee) {
970            match *node {
971                Callee::Super(..) => self.found = true,
972                _ => node.visit_children_with(self),
973            }
974        }
975
976        fn visit_super_prop_expr(&mut self, _: &SuperPropExpr) {
977            self.found = true
978        }
979    }
980
981    let pos = match body.iter().position(|s| match s {
982        Stmt::Expr(ExprStmt { expr, .. }) => matches!(
983            &**expr,
984            Expr::Call(CallExpr {
985                callee: Callee::Super(..),
986                ..
987            })
988        ),
989        _ => false,
990    }) {
991        Some(pos) => pos,
992        _ => return false,
993    };
994
995    let mut v = SuperFinder { found: false };
996    let body = &body[..pos];
997    v.visit_stmts(body);
998
999    !v.found
1000}
1001
1002fn escape_keywords(mut e: Box<Expr>) -> Box<Expr> {
1003    if let Expr::Fn(f) = &mut *e {
1004        if let Some(i) = &mut f.ident {
1005            let sym = Ident::verify_symbol(&i.sym);
1006
1007            if let Err(new) = sym {
1008                i.sym = new.into();
1009            }
1010        }
1011    }
1012
1013    e
1014}
1015
1016#[derive(Default)]
1017struct ClassFinder {
1018    found: bool,
1019}
1020
1021impl Visit for ClassFinder {
1022    noop_visit_type!(fail);
1023
1024    fn visit_class(&mut self, _: &Class) {
1025        self.found = true
1026    }
1027}
1028
1029impl Check for ClassFinder {
1030    fn should_handle(&self) -> bool {
1031        self.found
1032    }
1033}