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