swc_ecma_compat_es2022/class_properties/
mod.rs

1use rustc_hash::FxHashMap;
2use swc_atoms::{atom, Atom};
3use swc_common::{
4    errors::HANDLER, source_map::PURE_SP, util::take::Take, Mark, Span, Spanned, SyntaxContext,
5    DUMMY_SP,
6};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::{helper, perf::Check};
9use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
10use swc_ecma_transforms_macros::fast_path;
11use swc_ecma_utils::{
12    alias_ident_for, alias_if_required, constructor::inject_after_super,
13    default_constructor_with_span, is_literal, prepend_stmt, private_ident, quote_ident,
14    replace_ident, 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    class_name_tdz::ClassNameTdzFolder,
23    member_init::{MemberInit, MemberInitRecord, PrivAccessor, PrivMethod, PrivProp, PubProp},
24    private_field::{
25        dup_private_method, visit_private_in_expr, BrandCheckHandler, Private,
26        PrivateAccessVisitor, PrivateKind, PrivateRecord,
27    },
28    this_in_static::{NewTargetInProp, ThisInStaticFolder},
29    used_name::UsedNameCollector,
30};
31
32mod class_name_tdz;
33mod member_init;
34mod private_field;
35mod this_in_static;
36mod used_name;
37
38///
39///
40///
41///
42/// # Impl note
43///
44/// We use custom helper to handle export default class
45pub fn class_properties(config: Config, unresolved_mark: Mark) -> impl Pass {
46    visit_mut_pass(ClassProperties {
47        c: config,
48        private: PrivateRecord::new(),
49        extra: ClassExtra::default(),
50        unresolved_mark,
51    })
52}
53
54#[derive(Debug, Default, Clone, Copy)]
55pub struct Config {
56    pub private_as_properties: bool,
57    pub set_public_fields: bool,
58    pub constant_super: bool,
59    pub no_document_all: bool,
60    pub pure_getter: bool,
61}
62
63struct ClassProperties {
64    extra: ClassExtra,
65    c: Config,
66    private: PrivateRecord,
67    unresolved_mark: Mark,
68}
69
70#[derive(Default)]
71struct ClassExtra {
72    lets: Vec<VarDeclarator>,
73    vars: Vec<VarDeclarator>,
74    stmts: Vec<Stmt>,
75}
76
77#[swc_trace]
78impl ClassExtra {
79    fn prepend_with<T: StmtLike>(self, stmts: &mut Vec<T>) {
80        if !self.vars.is_empty() {
81            prepend_stmt(
82                stmts,
83                T::from(Stmt::from(VarDecl {
84                    span: DUMMY_SP,
85                    kind: VarDeclKind::Var,
86                    decls: self.vars,
87                    ..Default::default()
88                })),
89            )
90        }
91
92        if !self.lets.is_empty() {
93            prepend_stmt(
94                stmts,
95                T::from(Stmt::from(VarDecl {
96                    span: DUMMY_SP,
97                    kind: VarDeclKind::Let,
98                    decls: self.lets,
99                    ..Default::default()
100                })),
101            )
102        }
103
104        stmts.extend(self.stmts.into_iter().map(|stmt| stmt.into()))
105    }
106
107    fn merge_with<T: StmtLike>(self, stmts: &mut Vec<T>, class: T) {
108        if !self.vars.is_empty() {
109            stmts.push(T::from(Stmt::from(VarDecl {
110                span: DUMMY_SP,
111                kind: VarDeclKind::Var,
112                decls: self.vars,
113                ..Default::default()
114            })))
115        }
116
117        if !self.lets.is_empty() {
118            stmts.push(T::from(
119                VarDecl {
120                    span: DUMMY_SP,
121                    kind: VarDeclKind::Let,
122                    decls: self.lets,
123                    ..Default::default()
124                }
125                .into(),
126            ));
127        }
128
129        stmts.push(class);
130
131        stmts.extend(self.stmts.into_iter().map(|stmt| stmt.into()))
132    }
133}
134
135impl Take for ClassExtra {
136    fn dummy() -> Self {
137        Self::default()
138    }
139}
140
141#[swc_trace]
142#[fast_path(ShouldWork)]
143impl VisitMut for ClassProperties {
144    noop_visit_mut_type!(fail);
145
146    fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
147        self.visit_mut_stmt_like(n);
148
149        self.extra.take().prepend_with(n)
150    }
151
152    fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
153        let old = self.extra.take();
154        self.visit_mut_stmt_like(n);
155
156        self.extra.take().prepend_with(n);
157
158        self.extra = old;
159    }
160
161    fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
162        match body {
163            BlockStmtOrExpr::Expr(expr) if expr.is_class() => {
164                let ClassExpr { ident, class } = expr.take().class().unwrap();
165
166                let mut stmts = Vec::new();
167                let ident = ident.unwrap_or_else(|| private_ident!("_class"));
168                let (decl, extra) = self.visit_mut_class_as_decl(ident.clone(), class);
169
170                extra.merge_with(&mut stmts, decl.into());
171
172                stmts.push(
173                    ReturnStmt {
174                        span: DUMMY_SP,
175                        arg: Some(ident.into()),
176                    }
177                    .into(),
178                );
179
180                *body = BlockStmtOrExpr::BlockStmt(BlockStmt {
181                    span: DUMMY_SP,
182                    stmts,
183                    ..Default::default()
184                });
185            }
186            _ => body.visit_mut_children_with(self),
187        };
188    }
189
190    fn visit_mut_expr(&mut self, expr: &mut Expr) {
191        if let Expr::Class(ClassExpr {
192            ident: orig_ident,
193            class,
194        }) = expr
195        {
196            let ident = private_ident!(orig_ident
197                .clone()
198                .map(|id| Atom::from(format!("_{}", id.sym)))
199                .unwrap_or(atom!("_class")));
200            let (decl, ClassExtra { lets, vars, stmts }) =
201                self.visit_mut_class_as_decl(ident.clone(), class.take());
202
203            let class = ClassExpr {
204                ident: orig_ident.clone(),
205                class: decl.class,
206            }
207            .into();
208            if vars.is_empty() && lets.is_empty() && stmts.is_empty() {
209                *expr = class;
210                return;
211            }
212
213            let mut exprs = Vec::new();
214
215            for mut var in vars {
216                let init = var.init.take();
217                if let Some(init) = init {
218                    exprs.push(
219                        AssignExpr {
220                            span: var.span,
221                            op: op!("="),
222                            left: var.name.clone().try_into().unwrap(),
223                            right: init,
224                        }
225                        .into(),
226                    )
227                }
228                self.extra.vars.push(var);
229            }
230
231            for mut var in lets {
232                let init = var.init.take();
233                if let Some(init) = init {
234                    exprs.push(
235                        AssignExpr {
236                            span: var.span,
237                            op: op!("="),
238                            left: var.name.clone().try_into().unwrap(),
239                            right: init,
240                        }
241                        .into(),
242                    )
243                }
244                self.extra.lets.push(var);
245            }
246
247            let mut extra_value = false;
248            if !stmts.is_empty() {
249                extra_value = true;
250                self.extra.vars.push(VarDeclarator {
251                    span: DUMMY_SP,
252                    name: ident.clone().into(),
253                    init: None,
254                    definite: false,
255                });
256                exprs.push(
257                    AssignExpr {
258                        span: DUMMY_SP,
259                        left: ident.clone().into(),
260                        op: op!("="),
261                        right: class.into(),
262                    }
263                    .into(),
264                );
265            } else {
266                exprs.push(class.into());
267            }
268
269            for mut stmt in stmts {
270                if let Some(orig_ident) = orig_ident {
271                    replace_ident(&mut stmt, orig_ident.clone().into(), &ident);
272                }
273                match stmt {
274                    Stmt::Expr(e) => exprs.push(e.expr),
275                    Stmt::Decl(Decl::Var(v)) => {
276                        for mut decl in v.decls {
277                            let init = decl.init.take();
278
279                            if let Some(init) = init {
280                                exprs.push(
281                                    AssignExpr {
282                                        span: decl.span,
283                                        op: op!("="),
284                                        left: decl.name.clone().try_into().unwrap(),
285                                        right: init,
286                                    }
287                                    .into(),
288                                )
289                            }
290
291                            self.extra.vars.push(decl)
292                        }
293                    }
294                    _ => self.extra.stmts.push(stmt),
295                }
296            }
297
298            if extra_value {
299                exprs.push(Box::new(ident.into()))
300            }
301
302            *expr = SeqExpr {
303                span: DUMMY_SP,
304                exprs,
305            }
306            .into()
307        } else {
308            expr.visit_mut_children_with(self);
309        };
310    }
311}
312
313#[swc_trace]
314impl ClassProperties {
315    fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
316    where
317        T: StmtLike + ModuleItemLike + VisitMutWith<Self> + From<Stmt>,
318    {
319        let mut buf = Vec::with_capacity(stmts.len());
320
321        for stmt in stmts.drain(..) {
322            match T::try_into_stmt(stmt) {
323                Err(node) => match node.try_into_module_decl() {
324                    Ok(mut decl) => {
325                        match decl {
326                            ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
327                                span,
328                                decl: DefaultDecl::Class(ClassExpr { ident, class }),
329                                ..
330                            }) => {
331                                let ident = ident.unwrap_or_else(|| private_ident!("_class"));
332
333                                let (decl, extra) =
334                                    self.visit_mut_class_as_decl(ident.clone(), class);
335
336                                extra.merge_with(&mut buf, T::from(decl.into()));
337
338                                buf.push(
339                                    match T::try_from_module_decl(
340                                        NamedExport {
341                                            span,
342                                            specifiers: vec![ExportNamedSpecifier {
343                                                span: DUMMY_SP,
344                                                orig: ModuleExportName::Ident(ident),
345                                                exported: Some(ModuleExportName::Ident(
346                                                    private_ident!("default"),
347                                                )),
348                                                is_type_only: false,
349                                            }
350                                            .into()],
351                                            src: None,
352                                            type_only: false,
353                                            with: None,
354                                        }
355                                        .into(),
356                                    ) {
357                                        Ok(t) => t,
358                                        Err(..) => unreachable!(),
359                                    },
360                                );
361                            }
362                            ModuleDecl::ExportDecl(ExportDecl {
363                                span,
364                                decl:
365                                    Decl::Class(ClassDecl {
366                                        ident,
367                                        declare: false,
368                                        class,
369                                    }),
370                                ..
371                            }) => {
372                                let (decl, extra) = self.visit_mut_class_as_decl(ident, class);
373                                extra.merge_with(
374                                    &mut buf,
375                                    match T::try_from_module_decl(
376                                        ExportDecl {
377                                            span,
378                                            decl: decl.into(),
379                                        }
380                                        .into(),
381                                    ) {
382                                        Ok(t) => t,
383                                        Err(..) => unreachable!(),
384                                    },
385                                )
386                            }
387                            _ => {
388                                decl.visit_mut_children_with(self);
389                                buf.push(match T::try_from_module_decl(decl) {
390                                    Ok(t) => t,
391                                    Err(..) => unreachable!(),
392                                })
393                            }
394                        };
395                    }
396                    Err(..) => unreachable!(),
397                },
398                Ok(mut stmt) => {
399                    // Fold class
400                    match stmt {
401                        Stmt::Decl(Decl::Class(ClassDecl {
402                            ident,
403                            class,
404                            declare: false,
405                        })) => {
406                            let (decl, extra) = self.visit_mut_class_as_decl(ident, class);
407                            extra.merge_with(&mut buf, T::from(decl.into()))
408                        }
409                        _ => {
410                            stmt.visit_mut_children_with(self);
411                            buf.push(T::from(stmt))
412                        }
413                    }
414                }
415            }
416        }
417
418        *stmts = buf;
419    }
420}
421
422#[swc_trace]
423impl ClassProperties {
424    fn visit_mut_class_as_decl(
425        &mut self,
426        class_ident: Ident,
427        mut class: Box<Class>,
428    ) -> (ClassDecl, ClassExtra) {
429        // Create one mark per class
430        let private = Private {
431            mark: Mark::fresh(Mark::root()),
432            class_name: class_ident.clone(),
433            ident: {
434                let mut private_map = FxHashMap::default();
435
436                for member in class.body.iter() {
437                    match member {
438                        ClassMember::PrivateMethod(method) => {
439                            if let Some(kind) = private_map.get_mut(&method.key.name) {
440                                if dup_private_method(kind, method) {
441                                    let error =
442                                        format!("duplicate private name #{}.", method.key.name);
443                                    HANDLER.with(|handler| {
444                                        handler.struct_span_err(method.key.span, &error).emit()
445                                    });
446                                } else {
447                                    match method.kind {
448                                        MethodKind::Getter => kind.has_getter = true,
449                                        MethodKind::Setter => kind.has_setter = true,
450                                        MethodKind::Method => unreachable!(),
451                                    }
452                                }
453                            } else {
454                                private_map.insert(
455                                    method.key.name.clone(),
456                                    PrivateKind {
457                                        is_method: true,
458                                        is_static: method.is_static,
459                                        has_getter: method.kind == MethodKind::Getter,
460                                        has_setter: method.kind == MethodKind::Setter,
461                                    },
462                                );
463                            }
464                        }
465
466                        ClassMember::PrivateProp(prop) => {
467                            if private_map.contains_key(&prop.key.name) {
468                                let error = format!("duplicate private name #{}.", prop.key.name);
469                                HANDLER.with(|handler| {
470                                    handler.struct_span_err(prop.key.span, &error).emit()
471                                });
472                            } else {
473                                private_map.insert(
474                                    prop.key.name.clone(),
475                                    PrivateKind {
476                                        is_method: false,
477                                        is_static: prop.is_static,
478                                        has_getter: false,
479                                        has_setter: false,
480                                    },
481                                );
482                            };
483                        }
484
485                        _ => (),
486                    };
487                }
488
489                private_map
490            },
491        };
492
493        self.private.push(private);
494
495        // we must collect outer class's private first
496        class.visit_mut_children_with(self);
497
498        let has_super = class.super_class.is_some();
499
500        let mut constructor_inits = MemberInitRecord::new(self.c);
501        let mut vars = Vec::new();
502        let mut lets = Vec::new();
503        let mut extra_inits = MemberInitRecord::new(self.c);
504        let mut private_method_fn_decls = Vec::new();
505        let mut members = Vec::new();
506        let mut constructor = None;
507        let mut used_names = Vec::new();
508        let mut used_key_names = Vec::new();
509        let mut super_ident = None;
510
511        class.body.visit_mut_with(&mut BrandCheckHandler {
512            private: &self.private,
513        });
514
515        let should_create_vars_for_method_names = class.body.iter().any(|m| match m {
516            ClassMember::Constructor(_)
517            | ClassMember::PrivateMethod(_)
518            | ClassMember::TsIndexSignature(_)
519            | ClassMember::Empty(_) => false,
520
521            ClassMember::Method(m) => contains_super(&m.key),
522
523            ClassMember::ClassProp(_)
524            | ClassMember::AutoAccessor(_)
525            | ClassMember::PrivateProp(_)
526            | ClassMember::StaticBlock(_) => true,
527        });
528
529        for member in class.body {
530            match member {
531                ClassMember::Empty(..) | ClassMember::TsIndexSignature(..) => members.push(member),
532
533                ClassMember::Method(method) => {
534                    // we handle computed key here to preserve the execution order
535                    let key = match method.key {
536                        PropName::Computed(ComputedPropName {
537                            span: c_span,
538                            mut expr,
539                        }) if should_create_vars_for_method_names && !is_literal(&*expr) => {
540                            vars.extend(visit_private_in_expr(
541                                &mut expr,
542                                &self.private,
543                                self.c,
544                                self.unresolved_mark,
545                            ));
546
547                            expr.visit_mut_with(&mut ClassNameTdzFolder {
548                                class_name: &class_ident,
549                            });
550                            let ident = alias_ident_for(&expr, "tmp");
551                            // Handle computed property
552                            lets.push(VarDeclarator {
553                                span: DUMMY_SP,
554                                name: ident.clone().into(),
555                                init: Some(expr),
556                                definite: false,
557                            });
558                            // We use computed because `classes` pass converts PropName::Ident to
559                            // string.
560                            PropName::Computed(ComputedPropName {
561                                span: c_span,
562                                expr: ident.into(),
563                            })
564                        }
565                        _ => method.key,
566                    };
567                    members.push(ClassMember::Method(ClassMethod { key, ..method }))
568                }
569
570                ClassMember::ClassProp(mut prop) => {
571                    let prop_span = prop.span();
572                    prop.key.visit_mut_with(&mut ClassNameTdzFolder {
573                        class_name: &class_ident,
574                    });
575
576                    if !prop.is_static {
577                        prop.key.visit_with(&mut UsedNameCollector {
578                            used_names: &mut used_key_names,
579                        });
580
581                        prop.value.visit_with(&mut UsedNameCollector {
582                            used_names: &mut used_names,
583                        });
584                    }
585
586                    match &mut prop.key {
587                        PropName::Computed(key) if !is_literal(&key.expr) => {
588                            vars.extend(visit_private_in_expr(
589                                &mut key.expr,
590                                &self.private,
591                                self.c,
592                                self.unresolved_mark,
593                            ));
594                            let (ident, aliased) = if let Expr::Ident(i) = &*key.expr {
595                                if used_key_names.contains(&i.sym) {
596                                    (alias_ident_for(&key.expr, "_ref"), true)
597                                } else {
598                                    alias_if_required(&key.expr, "_ref")
599                                }
600                            } else {
601                                alias_if_required(&key.expr, "_ref")
602                            };
603                            if aliased {
604                                // Handle computed property
605                                lets.push(VarDeclarator {
606                                    span: DUMMY_SP,
607                                    name: ident.clone().into(),
608                                    init: Some(key.expr.take()),
609                                    definite: false,
610                                });
611                            }
612                            *key.expr = ident.into();
613                        }
614                        _ => (),
615                    };
616
617                    let mut value = prop.value.unwrap_or_else(|| Expr::undefined(prop_span));
618
619                    value.visit_mut_with(&mut NewTargetInProp);
620
621                    vars.extend(visit_private_in_expr(
622                        &mut value,
623                        &self.private,
624                        self.c,
625                        self.unresolved_mark,
626                    ));
627
628                    if prop.is_static {
629                        if let (Some(super_class), None) = (&mut class.super_class, &super_ident) {
630                            let (ident, aliased) = alias_if_required(&*super_class, "_ref");
631                            super_ident = Some(ident.clone());
632
633                            if aliased {
634                                vars.push(VarDeclarator {
635                                    span: DUMMY_SP,
636                                    name: ident.clone().into(),
637                                    init: None,
638                                    definite: false,
639                                });
640                                let span = super_class.span();
641                                **super_class = AssignExpr {
642                                    span,
643                                    op: op!("="),
644                                    left: ident.into(),
645                                    right: super_class.take(),
646                                }
647                                .into()
648                            }
649                        }
650
651                        value.visit_mut_with(&mut SuperFieldAccessFolder {
652                            class_name: &class_ident,
653                            constructor_this_mark: None,
654                            is_static: true,
655                            folding_constructor: false,
656                            in_injected_define_property_call: false,
657                            in_nested_scope: false,
658                            this_alias_mark: None,
659                            constant_super: self.c.constant_super,
660                            super_class: &super_ident,
661                            in_pat: false,
662                        });
663                        value.visit_mut_with(&mut ThisInStaticFolder {
664                            ident: class_ident.clone(),
665                        });
666                    }
667
668                    let init = MemberInit::PubProp(PubProp {
669                        span: prop_span,
670                        name: prop.key,
671                        value,
672                    });
673                    if prop.is_static {
674                        extra_inits.push(init);
675                    } else {
676                        constructor_inits.push(init);
677                    }
678                }
679                ClassMember::PrivateProp(mut prop) => {
680                    let prop_span = prop.span();
681
682                    let ident = Ident::new(
683                        format!("_{}", prop.key.name).into(),
684                        // We use `self.mark` for private variables.
685                        prop.key.span,
686                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
687                    );
688
689                    if let Some(value) = &mut prop.value {
690                        value.visit_mut_with(&mut NewTargetInProp);
691
692                        if prop.is_static {
693                            value.visit_mut_with(&mut SuperFieldAccessFolder {
694                                class_name: &class_ident,
695                                constructor_this_mark: None,
696                                is_static: true,
697                                folding_constructor: false,
698                                in_injected_define_property_call: false,
699                                in_nested_scope: false,
700                                this_alias_mark: None,
701                                constant_super: self.c.constant_super,
702                                super_class: &super_ident,
703                                in_pat: false,
704                            });
705                        }
706                        vars.extend(visit_private_in_expr(
707                            &mut *value,
708                            &self.private,
709                            self.c,
710                            self.unresolved_mark,
711                        ));
712                    }
713
714                    prop.value.visit_with(&mut UsedNameCollector {
715                        used_names: &mut used_names,
716                    });
717                    if prop.is_static {
718                        prop.value.visit_mut_with(&mut ThisInStaticFolder {
719                            ident: class_ident.clone(),
720                        });
721                    }
722
723                    let value = prop.value.unwrap_or_else(|| Expr::undefined(prop_span));
724
725                    if prop.is_static && prop.key.span.is_placeholder() {
726                        let init = MemberInit::StaticBlock(value);
727                        extra_inits.push(init);
728                        continue;
729                    }
730
731                    let init = MemberInit::PrivProp(PrivProp {
732                        span: prop_span,
733                        name: ident.clone(),
734                        value,
735                    });
736                    let span = PURE_SP;
737                    if self.c.private_as_properties {
738                        vars.push(VarDeclarator {
739                            span: DUMMY_SP,
740                            definite: false,
741                            name: ident.clone().into(),
742                            init: Some(
743                                CallExpr {
744                                    span,
745                                    callee: helper!(class_private_field_loose_key),
746                                    args: vec![ident.sym.as_arg()],
747                                    ..Default::default()
748                                }
749                                .into(),
750                            ),
751                        });
752                    } else if !prop.is_static {
753                        vars.push(VarDeclarator {
754                            span: DUMMY_SP,
755                            definite: false,
756                            name: ident.into(),
757                            init: Some(
758                                NewExpr {
759                                    span,
760                                    callee: Box::new(quote_ident!("WeakMap").into()),
761                                    args: Some(Default::default()),
762                                    ..Default::default()
763                                }
764                                .into(),
765                            ),
766                        });
767                    };
768                    if prop.is_static {
769                        extra_inits.push(init);
770                    } else {
771                        constructor_inits.push(init);
772                    };
773                }
774
775                ClassMember::Constructor(c) => {
776                    constructor = Some(c);
777                }
778
779                ClassMember::PrivateMethod(mut method) => {
780                    let is_static = method.is_static;
781                    let prop_span = method.span;
782
783                    let fn_name = Ident::new(
784                        match method.kind {
785                            MethodKind::Getter => format!("get_{}", method.key.name).into(),
786                            MethodKind::Setter => format!("set_{}", method.key.name).into(),
787                            MethodKind::Method => {
788                                if method.key.name.is_reserved_in_any() {
789                                    format!("__{}", method.key.name).into()
790                                } else {
791                                    method.key.name.clone()
792                                }
793                            }
794                        },
795                        method.span,
796                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
797                    );
798
799                    let weak_coll_var = Ident::new(
800                        format!("_{}", method.key.name).into(),
801                        // We use `self.mark` for private variables.
802                        method.key.span,
803                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
804                    );
805                    method.function.visit_with(&mut UsedNameCollector {
806                        used_names: &mut used_names,
807                    });
808
809                    let extra_collect = match (method.kind, is_static) {
810                        (MethodKind::Getter | MethodKind::Setter, false) => {
811                            let is_getter = method.kind == MethodKind::Getter;
812                            let inserted =
813                                constructor_inits.push(MemberInit::PrivAccessor(PrivAccessor {
814                                    span: prop_span,
815                                    name: weak_coll_var.clone(),
816                                    getter: if is_getter {
817                                        Some(fn_name.clone())
818                                    } else {
819                                        None
820                                    },
821                                    setter: if !is_getter {
822                                        Some(fn_name.clone())
823                                    } else {
824                                        None
825                                    },
826                                }));
827
828                            if inserted {
829                                Some(quote_ident!("WeakMap"))
830                            } else {
831                                None
832                            }
833                        }
834                        (MethodKind::Getter | MethodKind::Setter, true) => {
835                            let is_getter = method.kind == MethodKind::Getter;
836                            let inserted =
837                                extra_inits.push(MemberInit::PrivAccessor(PrivAccessor {
838                                    span: prop_span,
839                                    name: weak_coll_var.clone(),
840                                    getter: if is_getter {
841                                        Some(fn_name.clone())
842                                    } else {
843                                        None
844                                    },
845                                    setter: if !is_getter {
846                                        Some(fn_name.clone())
847                                    } else {
848                                        None
849                                    },
850                                }));
851                            if inserted && self.c.private_as_properties {
852                                Some(IdentName::default())
853                            } else {
854                                None
855                            }
856                        }
857
858                        (MethodKind::Method, false) => {
859                            constructor_inits.push(MemberInit::PrivMethod(PrivMethod {
860                                span: prop_span,
861                                name: weak_coll_var.clone(),
862                                fn_name: if self.c.private_as_properties {
863                                    fn_name.clone()
864                                } else {
865                                    Ident::dummy()
866                                },
867                            }));
868                            Some(quote_ident!("WeakSet"))
869                        }
870                        (MethodKind::Method, true) => {
871                            if self.c.private_as_properties {
872                                extra_inits.push(MemberInit::PrivMethod(PrivMethod {
873                                    span: prop_span,
874                                    name: weak_coll_var.clone(),
875                                    fn_name: fn_name.clone(),
876                                }));
877                                Some(Default::default())
878                            } else {
879                                None
880                            }
881                        }
882                    };
883
884                    if let Some(extra) = extra_collect {
885                        let span = PURE_SP;
886                        vars.push(VarDeclarator {
887                            span: DUMMY_SP,
888                            definite: false,
889                            name: weak_coll_var.clone().into(),
890                            init: Some(Box::new(if self.c.private_as_properties {
891                                CallExpr {
892                                    span,
893                                    callee: helper!(class_private_field_loose_key),
894                                    args: vec![weak_coll_var.sym.as_arg()],
895                                    ..Default::default()
896                                }
897                                .into()
898                            } else {
899                                NewExpr {
900                                    span,
901                                    callee: extra.into(),
902                                    args: Some(Default::default()),
903                                    ..Default::default()
904                                }
905                                .into()
906                            })),
907                        })
908                    };
909
910                    method.function.visit_mut_with(&mut SuperFieldAccessFolder {
911                        class_name: &class_ident,
912                        constructor_this_mark: None,
913                        is_static,
914                        folding_constructor: false,
915                        in_injected_define_property_call: false,
916                        in_nested_scope: false,
917                        this_alias_mark: None,
918                        constant_super: self.c.constant_super,
919                        super_class: &super_ident,
920                        in_pat: false,
921                    });
922
923                    private_method_fn_decls.push(
924                        FnDecl {
925                            ident: fn_name,
926                            function: method.function,
927                            declare: false,
928                        }
929                        .into(),
930                    )
931                }
932
933                ClassMember::StaticBlock(..) => {
934                    unreachable!("static_blocks pass should remove this")
935                }
936
937                ClassMember::AutoAccessor(..) => {
938                    unreachable!("auto_accessor pass should remove this")
939                }
940            }
941        }
942
943        let constructor =
944            self.process_constructor(class.span, constructor, has_super, constructor_inits);
945        if let Some(c) = constructor {
946            members.push(ClassMember::Constructor(c));
947        }
948
949        private_method_fn_decls.visit_mut_with(&mut PrivateAccessVisitor {
950            private: &self.private,
951            vars: Vec::new(),
952            private_access_type: Default::default(),
953            c: self.c,
954            unresolved_mark: self.unresolved_mark,
955        });
956
957        let mut extra_stmts = extra_inits.into_init_static(class_ident.clone());
958
959        extra_stmts.extend(private_method_fn_decls);
960
961        members.visit_mut_with(&mut PrivateAccessVisitor {
962            private: &self.private,
963            vars: Vec::new(),
964            private_access_type: Default::default(),
965            c: self.c,
966            unresolved_mark: self.unresolved_mark,
967        });
968
969        self.private.pop();
970
971        (
972            ClassDecl {
973                ident: class_ident,
974                declare: false,
975                class: Class {
976                    body: members,
977                    ..*class
978                }
979                .into(),
980            },
981            ClassExtra {
982                vars,
983                lets,
984                stmts: extra_stmts,
985            },
986        )
987    }
988
989    /// # Legacy support.
990    ///
991    /// ## Why is this required?
992    ///
993    /// Hygiene data of
994    ///
995    ///```ts
996    /// class A {
997    ///     b = this.a;
998    ///     constructor(a){
999    ///         this.a = a;
1000    ///     }
1001    /// }
1002    /// ```
1003    ///
1004    /// is
1005    ///
1006    ///```ts
1007    /// class A0 {
1008    ///     constructor(a1){
1009    ///         this.a0 = a0;
1010    ///         this.b0 = this.a0;
1011    ///     }
1012    /// }
1013    /// ```
1014    ///
1015    /// which is valid only for es2020 properties.
1016    ///
1017    /// Legacy proposal which is used by typescript requires different hygiene.
1018    #[allow(clippy::vec_box)]
1019    fn process_constructor(
1020        &mut self,
1021        class_span: Span,
1022        constructor: Option<Constructor>,
1023        has_super: bool,
1024        constructor_exprs: MemberInitRecord,
1025    ) -> Option<Constructor> {
1026        let constructor = constructor.or_else(|| {
1027            if constructor_exprs.record.is_empty() {
1028                None
1029            } else {
1030                Some(default_constructor_with_span(has_super, class_span))
1031            }
1032        });
1033
1034        if let Some(mut c) = constructor {
1035            let constructor_exprs = constructor_exprs.into_init();
1036            // Prepend properties
1037            inject_after_super(&mut c, constructor_exprs);
1038            Some(c)
1039        } else {
1040            None
1041        }
1042    }
1043}
1044
1045#[derive(Default)]
1046struct ShouldWork {
1047    found: bool,
1048}
1049
1050#[swc_trace]
1051impl Visit for ShouldWork {
1052    noop_visit_type!(fail);
1053
1054    fn visit_class_method(&mut self, _: &ClassMethod) {
1055        self.found = true;
1056    }
1057
1058    fn visit_class_prop(&mut self, _: &ClassProp) {
1059        self.found = true;
1060    }
1061
1062    fn visit_private_prop(&mut self, _: &PrivateProp) {
1063        self.found = true;
1064    }
1065
1066    fn visit_private_method(&mut self, _: &PrivateMethod) {
1067        self.found = true;
1068    }
1069
1070    fn visit_constructor(&mut self, _: &Constructor) {
1071        self.found = true;
1072    }
1073}
1074
1075impl Check for ShouldWork {
1076    fn should_handle(&self) -> bool {
1077        self.found
1078    }
1079}
1080
1081// TODO: remove
1082struct SuperVisitor {
1083    found: bool,
1084}
1085
1086impl Visit for SuperVisitor {
1087    noop_visit_type!(fail);
1088
1089    /// Don't recurse into constructor
1090    fn visit_constructor(&mut self, _: &Constructor) {}
1091
1092    /// Don't recurse into fn
1093    fn visit_fn_decl(&mut self, _: &FnDecl) {}
1094
1095    /// Don't recurse into fn
1096    fn visit_fn_expr(&mut self, _: &FnExpr) {}
1097
1098    /// Don't recurse into fn
1099    fn visit_function(&mut self, _: &Function) {}
1100
1101    /// Don't recurse into fn
1102    fn visit_getter_prop(&mut self, n: &GetterProp) {
1103        n.key.visit_with(self);
1104    }
1105
1106    /// Don't recurse into fn
1107    fn visit_method_prop(&mut self, n: &MethodProp) {
1108        n.key.visit_with(self);
1109        n.function.visit_with(self);
1110    }
1111
1112    /// Don't recurse into fn
1113    fn visit_setter_prop(&mut self, n: &SetterProp) {
1114        n.key.visit_with(self);
1115        n.param.visit_with(self);
1116    }
1117
1118    fn visit_super(&mut self, _: &Super) {
1119        self.found = true;
1120    }
1121}
1122
1123fn contains_super<N>(body: &N) -> bool
1124where
1125    N: VisitWith<SuperVisitor>,
1126{
1127    let mut visitor = SuperVisitor { found: false };
1128    body.visit_with(&mut visitor);
1129    visitor.found
1130}