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