1#![allow(clippy::vec_box)]
2
3use std::mem::take;
4
5use rustc_hash::FxHashSet;
6use swc_common::{util::take::Take, Mark, Spanned, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::assumptions::Assumptions;
9use swc_ecma_utils::{
10 default_constructor_with_span, prepend_stmt, private_ident, quote_ident, ExprFactory,
11};
12use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
13use swc_trace_macro::swc_trace;
14
15use crate::es2022::{
16 private_in_object::{ClassAnalyzer, ClassData, Mode},
17 static_blocks::generate_uid,
18};
19pub use crate::features::Features;
20
21mod es2020;
22mod es2021;
23mod es2022;
24mod features;
25
26#[derive(Debug)]
27pub struct Compiler {
28 config: Config,
29}
30
31impl Compiler {
32 pub fn new(config: Config) -> Self {
33 Self { config }
34 }
35}
36
37#[derive(Debug, Default)]
38pub struct Config {
39 pub assumptions: Assumptions,
40 pub includes: Features,
42 pub excludes: Features,
44}
45
46impl Pass for Compiler {
47 fn process(&mut self, program: &mut swc_ecma_ast::Program) {
48 program.visit_mut_with(&mut CompilerImpl::new(&self.config));
49 }
50}
51
52struct CompilerImpl<'a> {
53 config: &'a Config,
54
55 es2022_private_field_helper_vars: Vec<VarDeclarator>,
57 es2022_private_field_init_exprs: Vec<Box<Expr>>,
58 es2022_injected_weakset_vars: FxHashSet<Id>,
59 es2022_current_class_data: ClassData,
60
61 es2021_logical_assignment_vars: Vec<VarDeclarator>,
63}
64
65#[swc_trace]
66impl<'a> CompilerImpl<'a> {
67 fn new(config: &'a Config) -> Self {
68 Self {
69 config,
70 es2022_private_field_helper_vars: Vec::new(),
71 es2022_private_field_init_exprs: Vec::new(),
72 es2022_injected_weakset_vars: FxHashSet::default(),
73 es2022_current_class_data: ClassData::default(),
74 es2021_logical_assignment_vars: Vec::new(),
75 }
76 }
77
78 fn es2022_static_blocks_to_private_fields(&mut self, class: &mut Class) {
80 let mut private_names = FxHashSet::default();
81 for member in &class.body {
82 if let ClassMember::PrivateProp(private_property) = member {
83 private_names.insert(private_property.key.name.clone());
84 }
85 }
86
87 let mut count = 0;
88 for member in class.body.iter_mut() {
89 if let ClassMember::StaticBlock(static_block) = member {
90 if static_block.body.stmts.is_empty() {
91 *member = ClassMember::dummy();
92 continue;
93 }
94
95 let static_block_private_id = generate_uid(&private_names, &mut count);
96 *member = self
97 .transform_static_block(static_block.take(), static_block_private_id)
98 .into();
99 };
100 }
101 }
102
103 fn es2022_analyze_private_fields_for_in_operator(&mut self, class: &Class) {
106 class.visit_children_with(&mut ClassAnalyzer {
107 brand_check_names: &mut self.es2022_current_class_data.names_used_for_brand_checks,
108 ignore_class: true,
109 });
110
111 for m in &class.body {
112 match m {
113 ClassMember::PrivateMethod(m) => {
114 self.es2022_current_class_data
115 .privates
116 .insert(m.key.name.clone());
117 self.es2022_current_class_data
118 .methods
119 .push(m.key.name.clone());
120
121 if m.is_static {
122 self.es2022_current_class_data
123 .statics
124 .push(m.key.name.clone());
125 }
126 }
127
128 ClassMember::PrivateProp(m) => {
129 self.es2022_current_class_data
130 .privates
131 .insert(m.key.name.clone());
132
133 if m.is_static {
134 self.es2022_current_class_data
135 .statics
136 .push(m.key.name.clone());
137 }
138 }
139
140 _ => {}
141 }
142 }
143 }
144
145 fn es2022_inject_weakset_init_for_private_fields(&mut self, class: &mut Class) {
148 if self.es2022_current_class_data.constructor_exprs.is_empty() {
149 return;
150 }
151
152 let has_constructor = class
153 .body
154 .iter()
155 .any(|m| matches!(m, ClassMember::Constructor(_)));
156
157 if !has_constructor {
158 let has_super = class.super_class.is_some();
159 class
160 .body
161 .push(ClassMember::Constructor(default_constructor_with_span(
162 has_super, class.span,
163 )));
164 }
165
166 for m in &mut class.body {
167 if let ClassMember::Constructor(Constructor {
168 body: Some(body), ..
169 }) = m
170 {
171 for expr in take(&mut self.es2022_current_class_data.constructor_exprs) {
172 body.stmts.push(
173 ExprStmt {
174 span: DUMMY_SP,
175 expr,
176 }
177 .into(),
178 );
179 }
180 }
181 }
182 }
183
184 fn es2022_transform_private_in_to_weakset_has(&mut self, e: &mut Expr) -> bool {
186 if let Expr::Bin(BinExpr {
187 span,
188 op: op!("in"),
189 left,
190 right,
191 }) = e
192 {
193 if left.is_private_name() {
194 let left = left.take().expect_private_name();
195
196 let is_static = self.es2022_current_class_data.statics.contains(&left.name);
197 let is_method = self.es2022_current_class_data.methods.contains(&left.name);
198
199 if let Some(cls_ident) = self.es2022_current_class_data.ident.clone() {
200 if is_static && is_method {
201 *e = BinExpr {
202 span: *span,
203 op: op!("==="),
204 left: cls_ident.into(),
205 right: right.take(),
206 }
207 .into();
208 return true;
209 }
210 }
211
212 let var_name =
213 self.var_name_for_brand_check(&left, &self.es2022_current_class_data);
214
215 if self.es2022_current_class_data.privates.contains(&left.name)
216 && self.es2022_injected_weakset_vars.insert(var_name.to_id())
217 {
218 self.es2022_current_class_data.vars.push_var(
219 var_name.clone(),
220 Some(
221 NewExpr {
222 span: DUMMY_SP,
223 callee: Box::new(quote_ident!("WeakSet").into()),
224 args: Some(Default::default()),
225 ..Default::default()
226 }
227 .into(),
228 ),
229 );
230
231 if is_method {
232 self.es2022_current_class_data.constructor_exprs.push(
233 CallExpr {
234 span: DUMMY_SP,
235 callee: var_name
236 .clone()
237 .make_member(IdentName::new("add".into(), DUMMY_SP))
238 .as_callee(),
239 args: vec![ExprOrSpread {
240 spread: None,
241 expr: Box::new(ThisExpr { span: DUMMY_SP }.into()),
242 }],
243 ..Default::default()
244 }
245 .into(),
246 );
247 }
248 }
249
250 *e = CallExpr {
251 span: *span,
252 callee: var_name
253 .make_member(IdentName::new("has".into(), DUMMY_SP))
254 .as_callee(),
255 args: vec![ExprOrSpread {
256 spread: None,
257 expr: right.take(),
258 }],
259 ..Default::default()
260 }
261 .into();
262 return true;
263 }
264 }
265 false
266 }
267
268 fn es2022_prepend_private_field_vars(&mut self, stmts: &mut Vec<Stmt>) {
270 if self.es2022_private_field_helper_vars.is_empty() {
271 return;
272 }
273
274 prepend_stmt(
275 stmts,
276 VarDecl {
277 span: DUMMY_SP,
278 kind: VarDeclKind::Var,
279 declare: Default::default(),
280 decls: take(&mut self.es2022_private_field_helper_vars),
281 ..Default::default()
282 }
283 .into(),
284 );
285 }
286
287 fn es2022_prepend_private_field_vars_module(&mut self, items: &mut Vec<ModuleItem>) {
289 if self.es2022_private_field_helper_vars.is_empty() {
290 return;
291 }
292
293 prepend_stmt(
294 items,
295 VarDecl {
296 span: DUMMY_SP,
297 kind: VarDeclKind::Var,
298 declare: Default::default(),
299 decls: take(&mut self.es2022_private_field_helper_vars),
300 ..Default::default()
301 }
302 .into(),
303 );
304 }
305
306 fn es2022_add_weakset_to_private_props(&mut self, n: &mut PrivateProp) {
308 if !self
309 .es2022_current_class_data
310 .names_used_for_brand_checks
311 .contains(&n.key.name)
312 {
313 return;
314 }
315
316 let var_name = self.var_name_for_brand_check(&n.key, &self.es2022_current_class_data);
317
318 match &mut n.value {
319 Some(init) => {
320 let init_span = init.span();
321
322 let tmp = private_ident!("_tmp");
323
324 self.es2022_current_class_data
325 .vars
326 .push_var(tmp.clone(), None);
327
328 let assign = AssignExpr {
329 span: DUMMY_SP,
330 op: op!("="),
331 left: tmp.clone().into(),
332 right: init.take(),
333 }
334 .into();
335
336 let add_to_checker = CallExpr {
337 span: DUMMY_SP,
338 callee: var_name
339 .make_member(IdentName::new("add".into(), DUMMY_SP))
340 .as_callee(),
341 args: vec![ExprOrSpread {
342 spread: None,
343 expr: Box::new(ThisExpr { span: DUMMY_SP }.into()),
344 }],
345 ..Default::default()
346 }
347 .into();
348
349 *init = SeqExpr {
350 span: init_span,
351 exprs: vec![assign, add_to_checker, Box::new(tmp.into())],
352 }
353 .into();
354 }
355 None => {
356 n.value = Some(
357 UnaryExpr {
358 span: DUMMY_SP,
359 op: op!("void"),
360 arg: Box::new(
361 CallExpr {
362 span: DUMMY_SP,
363 callee: var_name
364 .make_member(IdentName::new("add".into(), DUMMY_SP))
365 .as_callee(),
366 args: vec![ExprOrSpread {
367 spread: None,
368 expr: Box::new(ThisExpr { span: DUMMY_SP }.into()),
369 }],
370 ..Default::default()
371 }
372 .into(),
373 ),
374 }
375 .into(),
376 )
377 }
378 }
379 }
380}
381
382#[swc_trace]
383impl<'a> VisitMut for CompilerImpl<'a> {
384 noop_visit_mut_type!(fail);
385
386 fn visit_mut_class(&mut self, class: &mut Class) {
387 if self.config.includes.contains(Features::STATIC_BLOCKS) {
389 self.es2022_static_blocks_to_private_fields(class);
390 }
391
392 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
393 self.es2022_analyze_private_fields_for_in_operator(class);
394 }
395
396 class.visit_mut_children_with(self);
398
399 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
401 self.es2022_inject_weakset_init_for_private_fields(class);
402 }
403 }
404
405 fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
406 let old_cls = if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
408 let old = take(&mut self.es2022_current_class_data);
409 self.es2022_current_class_data.mark = Mark::fresh(Mark::root());
410 self.es2022_current_class_data.ident = Some(n.ident.clone());
411 self.es2022_current_class_data.vars = Mode::ClassDecl {
412 vars: Default::default(),
413 };
414 Some(old)
415 } else {
416 None
417 };
418
419 n.visit_mut_children_with(self);
421
422 if let Some(old_cls) = old_cls {
424 match &mut self.es2022_current_class_data.vars {
425 Mode::ClassDecl { vars } => {
426 self.es2022_private_field_helper_vars.extend(take(vars));
427 }
428 _ => unreachable!(),
429 }
430 self.es2022_current_class_data = old_cls;
431 }
432 }
433
434 fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) {
435 let old_cls = if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
437 let old = take(&mut self.es2022_current_class_data);
438 self.es2022_current_class_data.mark = Mark::fresh(Mark::root());
439 self.es2022_current_class_data.ident.clone_from(&n.ident);
440 self.es2022_current_class_data.vars = Mode::ClassExpr {
441 vars: Default::default(),
442 init_exprs: Default::default(),
443 };
444 Some(old)
445 } else {
446 None
447 };
448
449 n.visit_mut_children_with(self);
451
452 if let Some(old_cls) = old_cls {
454 match &mut self.es2022_current_class_data.vars {
455 Mode::ClassExpr { vars, init_exprs } => {
456 self.es2022_private_field_helper_vars.extend(take(vars));
457 self.es2022_private_field_init_exprs
458 .extend(take(init_exprs));
459 }
460 _ => unreachable!(),
461 }
462 self.es2022_current_class_data = old_cls;
463 }
464 }
465
466 fn visit_mut_block_stmt(&mut self, s: &mut BlockStmt) {
468 s.visit_mut_children_with(self);
470 }
471
472 fn visit_mut_switch_case(&mut self, s: &mut SwitchCase) {
474 s.visit_mut_children_with(self);
475 }
476
477 fn visit_mut_assign_pat(&mut self, p: &mut AssignPat) {
478 p.left.visit_mut_with(self);
480
481 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
483 let mut buf = FxHashSet::default();
484 let mut v = ClassAnalyzer {
485 brand_check_names: &mut buf,
486 ignore_class: false,
487 };
488 p.right.visit_with(&mut v);
489
490 if !buf.is_empty() {
491 let mut bs = BlockStmt {
492 span: DUMMY_SP,
493 stmts: vec![ReturnStmt {
494 span: DUMMY_SP,
495 arg: Some(p.right.take()),
496 }
497 .into()],
498 ..Default::default()
499 };
500 bs.visit_mut_with(self);
501
502 p.right = CallExpr {
503 span: DUMMY_SP,
504 callee: ArrowExpr {
505 span: DUMMY_SP,
506 params: Default::default(),
507 body: Box::new(BlockStmtOrExpr::BlockStmt(bs)),
508 is_async: false,
509 is_generator: false,
510 ..Default::default()
511 }
512 .as_callee(),
513 args: Default::default(),
514 ..Default::default()
515 }
516 .into();
517 return;
518 }
519 }
520
521 p.right.visit_mut_with(self);
522 }
523
524 fn visit_mut_expr(&mut self, e: &mut Expr) {
525 let logical_transformed = self.config.includes.contains(Features::LOGICAL_ASSIGNMENTS)
528 && self.transform_logical_assignment(e);
529
530 let prev_prepend_exprs = if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
532 Some(take(&mut self.es2022_private_field_init_exprs))
533 } else {
534 None
535 };
536
537 e.visit_mut_children_with(self);
539
540 if let Some(prev_prepend_exprs) = prev_prepend_exprs {
543 let mut prepend_exprs = std::mem::replace(
544 &mut self.es2022_private_field_init_exprs,
545 prev_prepend_exprs,
546 );
547
548 if !prepend_exprs.is_empty() {
549 match e {
550 Expr::Seq(e) => {
551 e.exprs = prepend_exprs.into_iter().chain(e.exprs.take()).collect();
552 }
553 _ => {
554 prepend_exprs.push(Box::new(e.take()));
555 *e = SeqExpr {
556 span: DUMMY_SP,
557 exprs: prepend_exprs,
558 }
559 .into();
560 }
561 }
562 } else if !logical_transformed {
563 self.es2022_transform_private_in_to_weakset_has(e);
565 }
566 }
567 }
568
569 fn visit_mut_module_items(&mut self, ns: &mut Vec<ModuleItem>) {
570 if self
572 .config
573 .includes
574 .contains(Features::EXPORT_NAMESPACE_FROM)
575 {
576 self.transform_export_namespace_from(ns);
577 }
578
579 let need_var_hoisting = self.config.includes.contains(Features::LOGICAL_ASSIGNMENTS);
581
582 let saved_logical_vars = if need_var_hoisting {
583 self.es2021_logical_assignment_vars.take()
584 } else {
585 vec![]
586 };
587
588 ns.visit_mut_children_with(self);
590
591 if need_var_hoisting {
593 let logical_vars =
594 std::mem::replace(&mut self.es2021_logical_assignment_vars, saved_logical_vars);
595
596 let mut all_vars = Vec::new();
597 all_vars.extend(logical_vars);
598
599 if !all_vars.is_empty() {
600 prepend_stmt(
601 ns,
602 VarDecl {
603 span: DUMMY_SP,
604 kind: VarDeclKind::Var,
605 decls: all_vars,
606 ..Default::default()
607 }
608 .into(),
609 );
610 }
611 }
612
613 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT)
615 && !self.es2022_private_field_helper_vars.is_empty()
616 {
617 self.es2022_prepend_private_field_vars_module(ns);
618 }
619 }
620
621 fn visit_mut_private_prop(&mut self, n: &mut PrivateProp) {
622 n.visit_mut_children_with(self);
624
625 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT) {
627 self.es2022_add_weakset_to_private_props(n);
628 }
629 }
630
631 fn visit_mut_prop_name(&mut self, n: &mut PropName) {
632 if let PropName::Computed(_) = n {
633 n.visit_mut_children_with(self);
634 }
635 }
636
637 fn visit_mut_stmts(&mut self, s: &mut Vec<Stmt>) {
638 let need_var_hoisting = self.config.includes.contains(Features::LOGICAL_ASSIGNMENTS);
640
641 let saved_logical_vars = if need_var_hoisting {
642 self.es2021_logical_assignment_vars.take()
643 } else {
644 vec![]
645 };
646
647 s.visit_mut_children_with(self);
649
650 if need_var_hoisting {
652 let logical_vars =
653 std::mem::replace(&mut self.es2021_logical_assignment_vars, saved_logical_vars);
654
655 let mut all_vars = Vec::new();
656 all_vars.extend(logical_vars);
657
658 if !all_vars.is_empty() {
659 prepend_stmt(
660 s,
661 VarDecl {
662 span: DUMMY_SP,
663 kind: VarDeclKind::Var,
664 decls: all_vars,
665 ..Default::default()
666 }
667 .into(),
668 );
669 }
670 }
671
672 if self.config.includes.contains(Features::PRIVATE_IN_OBJECT)
674 && !self.es2022_private_field_helper_vars.is_empty()
675 {
676 self.es2022_prepend_private_field_vars(s);
677 }
678 }
679}