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