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#[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 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 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 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 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 if let Expr::Call(call) = &mut rhs {
347 let mut span = Span {
348 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 name: ident.into(),
364 definite: false,
365 }],
366 ..Default::default()
367 }
368 }
369
370 fn fold_class(&mut self, class_name: Option<Ident>, class: Box<Class>) -> Expr {
383 let span = class.span;
384
385 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 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 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 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 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 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 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 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 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 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 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 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#[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}