1#![allow(dead_code)]
2
3use std::time::Instant;
4
5use rustc_hash::FxHashSet;
6use swc_atoms::Atom;
7use swc_common::{util::take::Take, Span, Spanned, DUMMY_SP};
8use swc_ecma_ast::*;
9use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene};
10use swc_ecma_utils::{DropSpan, ModuleItemLike, StmtLike, Value};
11use swc_ecma_visit::{noop_visit_type, visit_mut_pass, visit_obj_and_computed, Visit, VisitWith};
12
13pub(crate) mod base54;
14pub(crate) mod size;
15pub(crate) mod sort;
16
17pub(crate) fn make_number(span: Span, value: f64) -> Expr {
18 trace_op!("Creating a numeric literal");
19 Lit::Num(Number {
20 span,
21 value,
22 raw: None,
23 })
24 .into()
25}
26
27pub trait ModuleItemExt:
28 StmtLike + ModuleItemLike + From<Stmt> + Spanned + std::fmt::Debug
29{
30 fn as_module_decl(&self) -> Result<&ModuleDecl, &Stmt>;
31
32 fn as_module_decl_mut(&mut self) -> Result<&mut ModuleDecl, &mut Stmt>;
33
34 fn from_module_item(item: ModuleItem) -> Self;
35
36 fn into_module_item(self) -> ModuleItem {
37 match self.into_module_decl() {
38 Ok(v) => v.into(),
39 Err(v) => v.into(),
40 }
41 }
42
43 fn into_module_decl(self) -> Result<ModuleDecl, Stmt>;
44}
45
46impl ModuleItemExt for Stmt {
47 fn as_module_decl(&self) -> Result<&ModuleDecl, &Stmt> {
48 Err(self)
49 }
50
51 fn as_module_decl_mut(&mut self) -> Result<&mut ModuleDecl, &mut Stmt> {
52 Err(self)
53 }
54
55 fn from_module_item(item: ModuleItem) -> Self {
56 item.expect_stmt()
57 }
58
59 fn into_module_decl(self) -> Result<ModuleDecl, Stmt> {
60 Err(self)
61 }
62}
63
64impl ModuleItemExt for ModuleItem {
65 fn as_module_decl(&self) -> Result<&ModuleDecl, &Stmt> {
66 match self {
67 ModuleItem::ModuleDecl(v) => Ok(v),
68 ModuleItem::Stmt(v) => Err(v),
69 #[cfg(swc_ast_unknown)]
70 _ => panic!("unable to access unknown nodes"),
71 }
72 }
73
74 fn as_module_decl_mut(&mut self) -> Result<&mut ModuleDecl, &mut Stmt> {
75 match self {
76 ModuleItem::ModuleDecl(v) => Ok(v),
77 ModuleItem::Stmt(v) => Err(v),
78 #[cfg(swc_ast_unknown)]
79 _ => panic!("unable to access unknown nodes"),
80 }
81 }
82
83 fn from_module_item(item: ModuleItem) -> Self {
84 item
85 }
86
87 fn into_module_decl(self) -> Result<ModuleDecl, Stmt> {
88 match self {
89 ModuleItem::ModuleDecl(v) => Ok(v),
90 ModuleItem::Stmt(v) => Err(v),
91 #[cfg(swc_ast_unknown)]
92 _ => panic!("unable to access unknown nodes"),
93 }
94 }
95}
96
97pub(crate) fn make_bool(span: Span, value: bool) -> Expr {
101 trace_op!("Creating a boolean literal");
102
103 UnaryExpr {
104 span,
105 op: op!("!"),
106 arg: Lit::Num(Number {
107 span: DUMMY_SP,
108 value: if value { 0.0 } else { 1.0 },
109 raw: None,
110 })
111 .into(),
112 }
113 .into()
114}
115
116pub(crate) trait ExprOptExt: Sized {
118 fn as_expr(&self) -> &Expr;
119 fn as_mut(&mut self) -> &mut Expr;
120
121 fn first_expr_mut(&mut self) -> &mut Expr {
122 let expr = self.as_mut();
123 match expr {
124 Expr::Seq(seq) => seq
125 .exprs
126 .first_mut()
127 .expect("Sequence expressions should have at least one element")
128 .first_expr_mut(),
129 expr => expr,
130 }
131 }
132
133 fn value_mut(&mut self) -> &mut Expr {
136 let expr = self.as_mut();
137 match expr {
138 Expr::Seq(seq) => seq
139 .exprs
140 .last_mut()
141 .expect("Sequence expressions should have at least one element")
142 .value_mut(),
143 expr => expr,
144 }
145 }
146
147 fn force_seq(&mut self) -> &mut SeqExpr {
148 let expr = self.as_mut();
149 match expr {
150 Expr::Seq(seq) => seq,
151 _ => {
152 let inner = expr.take();
153 *expr = SeqExpr {
154 span: DUMMY_SP,
155 exprs: vec![Box::new(inner)],
156 }
157 .into();
158 expr.force_seq()
159 }
160 }
161 }
162
163 fn prepend_exprs(&mut self, mut exprs: Vec<Box<Expr>>) {
164 if exprs.is_empty() {
165 return;
166 }
167
168 let to = self.as_mut();
169 match to {
170 Expr::Seq(to) => {
171 exprs.append(&mut to.exprs);
172 to.exprs = exprs;
173 }
174 _ => {
175 let v = to.take();
176 exprs.push(Box::new(v));
177 *to = SeqExpr {
178 span: DUMMY_SP,
179 exprs,
180 }
181 .into();
182 }
183 }
184 }
185}
186
187impl ExprOptExt for Box<Expr> {
188 fn as_expr(&self) -> &Expr {
189 self
190 }
191
192 fn as_mut(&mut self) -> &mut Expr {
193 self
194 }
195}
196
197impl ExprOptExt for Expr {
198 fn as_expr(&self) -> &Expr {
199 self
200 }
201
202 fn as_mut(&mut self) -> &mut Expr {
203 self
204 }
205}
206
207pub(crate) fn contains_leaping_continue_with_label<N>(n: &N, label: Atom) -> bool
208where
209 N: VisitWith<LeapFinder>,
210{
211 let mut v = LeapFinder {
212 target_label: Some(label),
213 ..Default::default()
214 };
215 n.visit_with(&mut v);
216 v.found_continue_with_label
217}
218
219#[allow(unused)]
220pub(crate) fn contains_leaping_yield<N>(n: &N) -> bool
221where
222 N: VisitWith<LeapFinder>,
223{
224 let mut v = LeapFinder::default();
225 n.visit_with(&mut v);
226 v.found_yield
227}
228
229#[derive(Default)]
230pub(crate) struct LeapFinder {
231 found_await: bool,
232 found_yield: bool,
233 found_continue_with_label: bool,
234 target_label: Option<Atom>,
235}
236
237impl Visit for LeapFinder {
238 noop_visit_type!(fail);
239
240 fn visit_await_expr(&mut self, n: &AwaitExpr) {
241 n.visit_children_with(self);
242
243 self.found_await = true;
244 }
245
246 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
247
248 fn visit_class_method(&mut self, _: &ClassMethod) {}
249
250 fn visit_constructor(&mut self, _: &Constructor) {}
251
252 fn visit_continue_stmt(&mut self, n: &ContinueStmt) {
253 n.visit_children_with(self);
254
255 if let Some(label) = &n.label {
256 self.found_continue_with_label |=
257 self.target_label.as_ref().is_some_and(|l| *l == label.sym);
258 }
259 }
260
261 fn visit_function(&mut self, _: &Function) {}
262
263 fn visit_getter_prop(&mut self, _: &GetterProp) {}
264
265 fn visit_setter_prop(&mut self, _: &SetterProp) {}
266
267 fn visit_yield_expr(&mut self, n: &YieldExpr) {
268 n.visit_children_with(self);
269
270 self.found_yield = true;
271 }
272}
273
274pub(crate) fn is_hoisted_var_decl_without_init<T>(t: &T) -> bool
276where
277 T: StmtLike,
278{
279 let var = match t.as_stmt() {
280 Some(Stmt::Decl(Decl::Var(v)))
281 if matches!(
282 &**v,
283 VarDecl {
284 kind: VarDeclKind::Var,
285 ..
286 }
287 ) =>
288 {
289 v
290 }
291 _ => return false,
292 };
293 var.decls.iter().all(|decl| decl.init.is_none())
294}
295
296pub(crate) trait IsModuleItem {
297 fn is_module_item() -> bool;
298}
299
300impl IsModuleItem for Stmt {
301 fn is_module_item() -> bool {
302 false
303 }
304}
305
306impl IsModuleItem for ModuleItem {
307 fn is_module_item() -> bool {
308 true
309 }
310}
311
312pub trait ValueExt<T>: Into<Value<T>> {
313 fn opt(self) -> Option<T> {
314 match self.into() {
315 Value::Known(v) => Some(v),
316 _ => None,
317 }
318 }
319}
320
321impl<T> ValueExt<T> for Value<T> {}
322
323pub struct DeepThisExprVisitor {
324 found: bool,
325}
326
327impl Visit for DeepThisExprVisitor {
328 noop_visit_type!(fail);
329
330 fn visit_this_expr(&mut self, _: &ThisExpr) {
331 self.found = true;
332 }
333}
334
335pub fn deeply_contains_this_expr<N>(body: &N) -> bool
336where
337 N: VisitWith<DeepThisExprVisitor>,
338{
339 let mut visitor = DeepThisExprVisitor { found: false };
340 body.visit_with(&mut visitor);
341 visitor.found
342}
343
344#[derive(Default)]
345pub(crate) struct IdentUsageCollector {
346 ids: FxHashSet<Id>,
347 ignore_nested: bool,
348}
349
350impl Visit for IdentUsageCollector {
351 noop_visit_type!(fail);
352
353 visit_obj_and_computed!();
354
355 fn visit_block_stmt_or_expr(&mut self, n: &BlockStmtOrExpr) {
356 if self.ignore_nested {
357 return;
358 }
359
360 n.visit_children_with(self);
361 }
362
363 fn visit_constructor(&mut self, n: &Constructor) {
364 if self.ignore_nested {
365 return;
366 }
367
368 n.visit_children_with(self);
369 }
370
371 fn visit_function(&mut self, n: &Function) {
372 if self.ignore_nested {
373 return;
374 }
375
376 n.visit_children_with(self);
377 }
378
379 fn visit_getter_prop(&mut self, n: &GetterProp) {
380 if self.ignore_nested {
381 return;
382 }
383
384 n.visit_children_with(self);
385 }
386
387 fn visit_setter_prop(&mut self, n: &SetterProp) {
388 if self.ignore_nested {
389 return;
390 }
391
392 n.visit_children_with(self);
393 }
394
395 fn visit_ident(&mut self, n: &Ident) {
396 self.ids.insert(n.to_id());
397 }
398
399 fn visit_prop_name(&mut self, n: &PropName) {
400 if let PropName::Computed(..) = n {
401 n.visit_children_with(self);
402 }
403 }
404}
405
406#[derive(Default)]
407pub(crate) struct CapturedIdCollector {
408 ids: FxHashSet<Id>,
409 is_nested: bool,
410}
411
412impl Visit for CapturedIdCollector {
413 noop_visit_type!(fail);
414
415 visit_obj_and_computed!();
416
417 fn visit_block_stmt_or_expr(&mut self, n: &BlockStmtOrExpr) {
418 let old = self.is_nested;
419 self.is_nested = true;
420 n.visit_children_with(self);
421 self.is_nested = old;
422 }
423
424 fn visit_constructor(&mut self, n: &Constructor) {
425 let old = self.is_nested;
426 self.is_nested = true;
427 n.visit_children_with(self);
428 self.is_nested = old;
429 }
430
431 fn visit_function(&mut self, n: &Function) {
432 let old = self.is_nested;
433 self.is_nested = true;
434 n.visit_children_with(self);
435 self.is_nested = old;
436 }
437
438 fn visit_ident(&mut self, n: &Ident) {
439 if self.is_nested {
440 self.ids.insert(n.to_id());
441 }
442 }
443
444 fn visit_prop_name(&mut self, n: &PropName) {
445 if let PropName::Computed(..) = n {
446 n.visit_children_with(self);
447 }
448 }
449}
450
451pub(crate) fn idents_captured_by<N>(n: &N) -> FxHashSet<Id>
452where
453 N: VisitWith<CapturedIdCollector>,
454{
455 let mut v = CapturedIdCollector {
456 is_nested: false,
457 ..Default::default()
458 };
459 n.visit_with(&mut v);
460 v.ids
461}
462
463pub(crate) fn idents_used_by<N>(n: &N) -> FxHashSet<Id>
464where
465 N: VisitWith<IdentUsageCollector>,
466{
467 let mut v = IdentUsageCollector {
468 ignore_nested: false,
469 ..Default::default()
470 };
471 n.visit_with(&mut v);
472 v.ids
473}
474
475pub(crate) fn idents_used_by_ignoring_nested<N>(n: &N) -> FxHashSet<Id>
476where
477 N: VisitWith<IdentUsageCollector>,
478{
479 let mut v = IdentUsageCollector {
480 ignore_nested: true,
481 ..Default::default()
482 };
483 n.visit_with(&mut v);
484 v.ids
485}
486
487pub fn now() -> Option<Instant> {
488 #[cfg(target_arch = "wasm32")]
489 {
490 None
491 }
492 #[cfg(not(target_arch = "wasm32"))]
493 {
494 Some(Instant::now())
495 }
496}
497
498#[allow(unused)]
499pub(crate) fn dump_program(p: &Program) -> String {
500 #[cfg(feature = "debug")]
501 {
502 force_dump_program(p)
503 }
504 #[cfg(not(feature = "debug"))]
505 {
506 String::new()
507 }
508}
509
510pub(crate) fn force_dump_program(p: &Program) -> String {
511 let _noop_sub = tracing::subscriber::set_default(tracing::subscriber::NoSubscriber::default());
512
513 crate::debug::dump(
514 &p.clone()
515 .apply(fixer(None))
516 .apply(hygiene())
517 .apply(visit_mut_pass(DropSpan {})),
518 true,
519 )
520}
521
522#[cfg(feature = "concurrent")]
523#[macro_export(local_inner_macros)]
524#[allow(clippy::crate_in_macro_def)]
525macro_rules! maybe_par {
526 ($prefix:ident.$name:ident.iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
527 if $prefix.$name.len() >= $threshold {
528 use par_iter::prelude::*;
529 $prefix.$name.par_iter().$operator($($rest)*)
530 } else {
531 $prefix.$name.iter().$operator($($rest)*)
532 }
533 };
534
535 ($prefix:ident.$name:ident.into_iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
536 if $prefix.$name.len() >= $threshold {
537 use par_iter::prelude::*;
538 $prefix.$name.into_par_iter().$operator($($rest)*)
539 } else {
540 $prefix.$name.into_iter().$operator($($rest)*)
541 }
542 };
543
544 ($name:ident.iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
545 if $name.len() >= $threshold {
546 use par_iter::prelude::*;
547 $name.par_iter().$operator($($rest)*)
548 } else {
549 $name.iter().$operator($($rest)*)
550 }
551 };
552
553 ($name:ident.into_iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
554 if $name.len() >= $threshold {
555 use par_iter::prelude::*;
556 $name.into_par_iter().$operator($($rest)*)
557 } else {
558 $name.into_iter().$operator($($rest)*)
559 }
560 };
561
562 ($name:ident.iter_mut().$operator:ident($($rest:expr)*), $threshold:expr) => {
563 if $name.len() >= $threshold {
564 use par_iter::prelude::*;
565 $name.par_iter_mut().$operator($($rest)*)
566 } else {
567 $name.iter_mut().$operator($($rest)*)
568 }
569 };
570
571 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
572 if $name.len() >= $threshold {
573 use par_iter::prelude::*;
574 $name.par_iter().$operator($($rest)*).$operator2($($rest2)*)
575 } else {
576 $name.iter().$operator($($rest)*).$operator2($($rest2)*)
577 }
578 };
579
580 ($name:ident.into_iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
581 if $name.len() >= $threshold {
582 use par_iter::prelude::*;
583 $name.into_par_iter().$operator($($rest)*).$operator2($($rest2)*)
584 } else {
585 $name.into_iter().$operator($($rest)*).$operator2($($rest2)*)
586 }
587 };
588
589 ($name:ident.iter_mut().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
590 if $name.len() >= $threshold {
591 use par_iter::prelude::*;
592 $name.par_iter_mut().$operator($($rest)*).$operator2($($rest2)*)
593 } else {
594 $name.iter_mut().$operator($($rest)*).$operator2($($rest2)*)
595 }
596 };
597
598 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident::<$t:ty>($($rest2:expr)*), $threshold:expr) => {
599 if $name.len() >= $threshold {
600 use par_iter::prelude::*;
601 $name.par_iter().$operator($($rest)*).$operator2::<$t>($($rest2)*)
602 } else {
603 $name.iter().$operator($($rest)*).$operator2::<$t>($($rest2)*)
604 }
605 };
606
607 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*).$operator3:ident($($rest3:expr)*), $threshold:expr) => {
608 if $name.len() >= $threshold {
609 use par_iter::prelude::*;
610 $name.par_iter().$operator($($rest)*).$operator2($($rest2)*).$operator3($($rest3)*)
611 } else {
612 $name.iter().$operator($($rest)*).$operator2($($rest2)*).$operator3($($rest3)*)
613 }
614 };
615}
616
617#[cfg(not(feature = "concurrent"))]
618#[macro_export(local_inner_macros)]
619#[allow(clippy::crate_in_macro_def)]
620macro_rules! maybe_par {
621 ($prefix:ident.$name:ident.iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
622 $prefix.$name.iter().$operator($($rest)*)
623 };
624
625 ($prefix:ident.$name:ident.into_iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
626 $prefix.$name.into_iter().$operator($($rest)*)
627 };
628
629 ($name:ident.iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
630 $name.iter().$operator($($rest)*)
631 };
632
633 ($name:ident.into_iter().$operator:ident($($rest:expr)*), $threshold:expr) => {
634 $name.into_iter().$operator($($rest)*)
635 };
636
637 ($name:ident.iter_mut().$operator:ident($($rest:expr)*), $threshold:expr) => {
638 $name.iter_mut().$operator($($rest)*)
639 };
640
641 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
642 $name.iter().$operator($($rest)*).$operator2($($rest2)*)
643 };
644
645 ($name:ident.into_iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
646 $name.into_iter().$operator($($rest)*).$operator2($($rest2)*)
647 };
648
649 ($name:ident.iter_mut().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*), $threshold:expr) => {
650 $name.iter_mut().$operator($($rest)*).$operator2($($rest2)*)
651 };
652
653 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident::<$t:ty>($($rest2:expr)*), $threshold:expr) => {
654 $name.iter().$operator($($rest)*).$operator2::<$t>($($rest2)*)
655 };
656
657 ($name:ident.iter().$operator:ident($($rest:expr)*).$operator2:ident($($rest2:expr)*).$operator3:ident($($rest3:expr)*), $threshold:expr) => {
658 $name.iter().$operator($($rest)*).$operator2($($rest2)*).$operator3($($rest3)*)
659 };
660}