1use std::{
2 borrow::Borrow,
3 mem::take,
4 ops::{Deref, DerefMut},
5};
6
7use rustc_hash::{FxHashMap, FxHashSet};
8use swc_atoms::{Atom, Wtf8Atom};
9use swc_common::{util::take::Take, Mark, SyntaxContext, DUMMY_SP};
10use swc_ecma_ast::*;
11use swc_ecma_transforms_base::perf::{Parallel, ParallelExt};
12use swc_ecma_utils::{collect_decls, contains_this_expr, ExprCtx, ExprExt, Remapper};
13use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
14use tracing::debug;
15
16use super::{Ctx, Optimizer};
17use crate::HEAVY_TASK_PARALLELS;
18
19impl<'b> Optimizer<'b> {
20 pub(super) fn normalize_expr(&mut self, e: &mut Expr) {
21 match e {
22 Expr::Seq(seq) => {
23 for e in &mut seq.exprs {
24 self.normalize_expr(e);
25 }
26
27 if seq.exprs.len() == 1 {
28 *e = *seq.exprs.take().into_iter().next().unwrap();
29 self.normalize_expr(e);
30 return;
31 }
32
33 if seq.exprs.iter().any(|v| v.is_seq()) {
34 let mut new = Vec::new();
35
36 for e in seq.exprs.take() {
37 match *e {
38 Expr::Seq(s) => {
39 new.extend(s.exprs);
40 }
41 _ => new.push(e),
42 }
43 }
44
45 seq.exprs = new;
46 }
47 }
48
49 Expr::Cond(cond) => {
50 self.normalize_expr(&mut cond.test);
51 self.normalize_expr(&mut cond.cons);
52 self.normalize_expr(&mut cond.alt);
53 }
54
55 Expr::Assign(a) => {
56 self.normalize_expr(&mut a.right);
57 }
58
59 _ => {}
60 }
61 }
62
63 pub(super) fn access_numeric_property<'e>(
64 &mut self,
65 _expr: &'e mut Expr,
66 _idx: usize,
67 ) -> Option<&'e mut Expr> {
68 None
69 }
70
71 pub(super) fn has_const_ann(&self, ctxt: SyntaxContext) -> bool {
73 ctxt.has_mark(self.marks.const_ann)
74 }
75
76 pub(super) fn has_noinline(&self, ctxt: SyntaxContext) -> bool {
78 ctxt.has_mark(self.marks.noinline)
79 }
80
81 pub(super) fn with_ctx(&mut self, mut ctx: Ctx) -> WithCtx<'_, 'b> {
83 let mut scope_ctxt = ctx.scope;
84
85 if self.ctx.scope != scope_ctxt {
86 if scope_ctxt.clone().remove_mark() == self.marks.fake_block {
87 scope_ctxt.remove_mark();
88 }
89 ctx.scope = scope_ctxt;
90
91 #[cfg(debug_assertions)]
92 {
93 self.data.scopes.get(&scope_ctxt).unwrap_or_else(|| {
94 panic!("scope not found: {:?}; {:#?}", scope_ctxt, self.data.scopes)
95 });
96 }
97 }
98
99 let orig_ctx = std::mem::replace(&mut self.ctx, ctx);
100 WithCtx {
101 reducer: self,
102 orig_ctx,
103 }
104 }
105
106 pub(super) fn try_remove_label(&mut self, s: &mut LabeledStmt) {
107 if !self.options.dead_code {
108 return;
109 }
110
111 let mut analyer = LabelAnalyzer {
112 label: s.label.sym.clone(),
113 ..Default::default()
114 };
115
116 match &mut *s.body {
117 Stmt::For(ForStmt { body, .. })
118 | Stmt::ForIn(ForInStmt { body, .. })
119 | Stmt::ForOf(ForOfStmt { body, .. })
120 | Stmt::While(WhileStmt { body, .. })
121 | Stmt::DoWhile(DoWhileStmt { body, .. }) => {
122 analyer.top_breakable = true;
123 body.visit_mut_with(&mut analyer)
124 }
125 Stmt::Switch(SwitchStmt { cases, .. }) => {
126 analyer.top_breakable = true;
127 cases.visit_mut_with(&mut analyer)
128 }
129 _ => s.body.visit_mut_with(&mut analyer),
130 };
131
132 if analyer.count == 0 {
133 let _label = s.label.take();
134 self.changed = true;
135 report_change!("Removing label `{}`", _label);
136 }
137 }
138}
139
140pub(super) struct WithCtx<'a, 'b> {
141 reducer: &'a mut Optimizer<'b>,
142 orig_ctx: Ctx,
143}
144
145impl<'b> Deref for WithCtx<'_, 'b> {
146 type Target = Optimizer<'b>;
147
148 fn deref(&self) -> &Self::Target {
149 self.reducer
150 }
151}
152
153impl DerefMut for WithCtx<'_, '_> {
154 fn deref_mut(&mut self) -> &mut Self::Target {
155 self.reducer
156 }
157}
158
159impl Drop for WithCtx<'_, '_> {
160 fn drop(&mut self) {
161 self.reducer.ctx = self.orig_ctx.clone();
162 }
163}
164
165pub(crate) fn extract_class_side_effect(
166 expr_ctx: ExprCtx,
167 c: &mut Class,
168) -> Option<Vec<&mut Box<Expr>>> {
169 let mut res = Vec::new();
170 if let Some(e) = &mut c.super_class {
171 if e.may_have_side_effects(expr_ctx) {
172 res.push(e);
173 }
174 }
175
176 for m in &mut c.body {
177 match m {
178 ClassMember::Method(ClassMethod {
179 key: PropName::Computed(key),
180 ..
181 }) => {
182 if key.expr.may_have_side_effects(expr_ctx) {
183 res.push(&mut key.expr);
184 }
185 }
186
187 ClassMember::ClassProp(p) => {
188 if let PropName::Computed(key) = &mut p.key {
189 if key.expr.may_have_side_effects(expr_ctx) {
190 res.push(&mut key.expr);
191 }
192 }
193
194 if let Some(v) = &mut p.value {
195 if p.is_static && v.may_have_side_effects(expr_ctx) {
196 if contains_this_expr(v) {
197 return None;
198 }
199 res.push(v);
200 }
201 }
202 }
203 ClassMember::PrivateProp(PrivateProp {
204 value: Some(v),
205 is_static: true,
206 ..
207 }) => {
208 if v.may_have_side_effects(expr_ctx) {
209 if contains_this_expr(v) {
210 return None;
211 }
212 res.push(v);
213 }
214 }
215
216 _ => {}
217 }
218 }
219
220 Some(res)
221}
222
223pub(crate) fn is_valid_for_lhs(e: &Expr) -> bool {
224 !matches!(e, Expr::Lit(..) | Expr::Unary(..))
225}
226
227#[derive(Clone, Copy)]
231pub(crate) struct Finalizer<'a> {
232 pub simple_functions: &'a FxHashMap<Id, Box<Expr>>,
233 pub lits: &'a FxHashMap<Id, Box<Expr>>,
234 pub lits_for_cmp: &'a FxHashMap<Id, Box<Expr>>,
235 pub lits_for_array_access: &'a FxHashMap<Id, Box<Expr>>,
236 pub hoisted_props: &'a FxHashMap<(Id, Wtf8Atom), Ident>,
237
238 pub vars_to_remove: &'a FxHashSet<Id>,
239
240 pub changed: bool,
241}
242
243impl Parallel for Finalizer<'_> {
244 fn create(&self) -> Self {
245 *self
246 }
247
248 fn merge(&mut self, other: Self) {
249 self.changed |= other.changed;
250 }
251}
252
253impl Finalizer<'_> {
254 fn var(&mut self, i: &Id, mode: FinalizerMode) -> Option<Box<Expr>> {
255 let mut e = match mode {
256 FinalizerMode::Callee => {
257 let mut value = self.simple_functions.get(i).cloned()?;
258 let mut cache = FxHashMap::default();
259 let mut remap = FxHashMap::default();
260 let bindings: FxHashSet<Id> = collect_decls(&*value);
261 let new_mark = Mark::new();
262
263 for id in bindings {
265 let new_ctxt = cache
266 .entry(id.1)
267 .or_insert_with(|| id.1.apply_mark(new_mark));
268
269 let new_ctxt = *new_ctxt;
270
271 remap.insert(id, new_ctxt);
272 }
273
274 if !remap.is_empty() {
275 let mut remapper = Remapper::new(&remap);
276 value.visit_mut_with(&mut remapper);
277 }
278
279 value
280 }
281 FinalizerMode::ComparisonWithLit => self.lits_for_cmp.get(i).cloned()?,
282 FinalizerMode::MemberAccess => self.lits_for_array_access.get(i).cloned()?,
283 };
284
285 e.visit_mut_children_with(self);
286
287 match &*e {
288 Expr::Ident(Ident { sym, .. }) if &**sym == "eval" => Some(
289 SeqExpr {
290 span: DUMMY_SP,
291 exprs: vec![0.into(), e],
292 }
293 .into(),
294 ),
295 _ => Some(e),
296 }
297 }
298
299 fn check(&mut self, e: &mut Expr, mode: FinalizerMode) {
300 if let Expr::Ident(i) = e {
301 if let Some(new) = self.var(&i.to_id(), mode) {
302 debug!("multi-replacer: Replaced `{}`", i);
303 self.changed = true;
304
305 *e = *new;
306 }
307 }
308 }
309}
310
311#[derive(Debug, Clone, Copy)]
312enum FinalizerMode {
313 Callee,
314 ComparisonWithLit,
315 MemberAccess,
316}
317
318impl VisitMut for Finalizer<'_> {
319 noop_visit_mut_type!(fail);
320
321 fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
322 e.visit_mut_children_with(self);
323
324 match e.op {
325 op!("===") | op!("!==") | op!("==") | op!("!=") => {
326 if e.left.is_lit() {
328 self.check(&mut e.right, FinalizerMode::ComparisonWithLit);
329 } else if e.right.is_lit() {
330 self.check(&mut e.left, FinalizerMode::ComparisonWithLit);
331 }
332 }
333 _ => {}
334 }
335 }
336
337 fn visit_mut_callee(&mut self, e: &mut Callee) {
338 e.visit_mut_children_with(self);
339
340 if let Callee::Expr(e) = e {
341 self.check(e, FinalizerMode::Callee);
342 }
343 }
344
345 fn visit_mut_class_members(&mut self, members: &mut Vec<ClassMember>) {
346 self.maybe_par(*HEAVY_TASK_PARALLELS, members, |v, member| {
347 member.visit_mut_with(v);
348 });
349 }
350
351 fn visit_mut_expr(&mut self, n: &mut Expr) {
352 match n {
353 Expr::Ident(i) => {
354 if let Some(expr) = self.lits.get(&i.to_id()) {
355 *n = *expr.clone();
356 return;
357 }
358 }
359 Expr::Member(e) => {
360 if let Expr::Ident(obj) = &*e.obj {
361 let sym = match &e.prop {
362 MemberProp::Ident(i) => i.sym.borrow(),
363 MemberProp::Computed(e) => match &*e.expr {
364 Expr::Ident(ident) => ident.sym.borrow(),
365 Expr::Lit(Lit::Str(s)) => &s.value,
366 _ => return,
367 },
368 _ => return,
369 };
370
371 if let Some(ident) = self.hoisted_props.get(&(obj.to_id(), sym.clone())) {
372 self.changed = true;
373 *n = ident.clone().into();
374 return;
375 }
376 }
377 }
378 _ => {}
379 }
380
381 n.visit_mut_children_with(self);
382 }
383
384 fn visit_mut_expr_or_spreads(&mut self, n: &mut Vec<ExprOrSpread>) {
385 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
386 n.visit_mut_with(v);
387 });
388 }
389
390 fn visit_mut_exprs(&mut self, n: &mut Vec<Box<Expr>>) {
391 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
392 n.visit_mut_with(v);
393 });
394 }
395
396 fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
397 e.visit_mut_children_with(self);
398
399 if let MemberProp::Computed(ref mut prop) = e.prop {
400 if let Expr::Lit(Lit::Num(..)) = &*prop.expr {
401 self.check(&mut e.obj, FinalizerMode::MemberAccess);
402 }
403 }
404 }
405
406 fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
407 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
408 n.visit_mut_with(v);
409 });
410 }
411
412 fn visit_mut_opt_var_decl_or_expr(&mut self, n: &mut Option<VarDeclOrExpr>) {
413 n.visit_mut_children_with(self);
414
415 if let Some(VarDeclOrExpr::VarDecl(v)) = n {
416 if v.decls.is_empty() {
417 *n = None;
418 }
419 }
420 }
421
422 fn visit_mut_opt_vec_expr_or_spreads(&mut self, n: &mut Vec<Option<ExprOrSpread>>) {
423 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
424 n.visit_mut_with(v);
425 });
426 }
427
428 fn visit_mut_prop_or_spreads(&mut self, n: &mut Vec<PropOrSpread>) {
429 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
430 n.visit_mut_with(v);
431 });
432 }
433
434 fn visit_mut_stmt(&mut self, n: &mut Stmt) {
435 n.visit_mut_children_with(self);
436
437 if let Stmt::Decl(Decl::Var(v)) = n {
438 if v.decls.is_empty() {
439 n.take();
440 }
441 }
442 }
443
444 fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
445 self.maybe_par(*HEAVY_TASK_PARALLELS, n, |v, n| {
446 n.visit_mut_with(v);
447 });
448 }
449
450 fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
451 n.visit_mut_children_with(self);
452
453 if n.init.is_none() {
454 if let Pat::Ident(i) = &n.name {
455 if self.vars_to_remove.contains(&i.to_id()) {
456 n.name.take();
457 }
458 }
459 }
460 }
461
462 fn visit_mut_var_declarators(&mut self, n: &mut Vec<VarDeclarator>) {
463 n.visit_mut_children_with(self);
464
465 n.retain(|v| !v.name.is_invalid());
466 }
467
468 fn visit_mut_prop(&mut self, n: &mut Prop) {
469 n.visit_mut_children_with(self);
470
471 if let Prop::Shorthand(i) = n {
472 if let Some(expr) = self.lits.get(&i.to_id()) {
473 let key = prop_name_from_ident(i.take());
474 *n = Prop::KeyValue(KeyValueProp {
475 key,
476 value: expr.clone(),
477 });
478 self.changed = true;
479 }
480 }
481 }
482}
483
484pub(crate) struct NormalMultiReplacer<'a> {
485 pub vars: &'a mut FxHashMap<Id, Box<Expr>>,
486 pub changed: bool,
487 should_consume: bool,
488}
489
490impl<'a> NormalMultiReplacer<'a> {
491 pub fn new(vars: &'a mut FxHashMap<Id, Box<Expr>>, should_consume: bool) -> Self {
493 NormalMultiReplacer {
494 vars,
495 should_consume,
496 changed: false,
497 }
498 }
499
500 fn var(&mut self, i: &Id) -> Option<Box<Expr>> {
501 let mut e = self.vars.remove(i)?;
502
503 e.visit_mut_children_with(self);
504
505 let e = if self.should_consume {
506 e
507 } else {
508 let new_e = e.clone();
509 self.vars.insert(i.clone(), e);
510
511 new_e
512 };
513
514 match &*e {
515 Expr::Ident(Ident { sym, .. }) if &**sym == "eval" => Some(
516 SeqExpr {
517 span: DUMMY_SP,
518 exprs: vec![0.into(), e],
519 }
520 .into(),
521 ),
522 _ => Some(e),
523 }
524 }
525}
526
527impl VisitMut for NormalMultiReplacer<'_> {
528 noop_visit_mut_type!(fail);
529
530 fn visit_mut_expr(&mut self, e: &mut Expr) {
531 if self.vars.is_empty() {
532 return;
533 }
534 e.visit_mut_children_with(self);
535
536 if self.vars.is_empty() {
537 return;
538 }
539
540 if let Expr::Ident(i) = e {
541 if let Some(new) = self.var(&i.to_id()) {
542 debug!("multi-replacer: Replaced `{}`", i);
543 self.changed = true;
544
545 *e = *new;
546 }
547 }
548 }
549
550 fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
551 if self.vars.is_empty() {
552 return;
553 }
554 items.visit_mut_children_with(self);
555
556 #[cfg(feature = "debug")]
557 if !self.vars.is_empty() {
558 let keys = self.vars.iter().map(|(k, _)| k.clone()).collect::<Vec<_>>();
559 debug!("Dropping {:?}", keys);
560 }
561 }
562
563 fn visit_mut_prop(&mut self, p: &mut Prop) {
564 p.visit_mut_children_with(self);
565
566 if let Prop::Shorthand(i) = p {
567 if let Some(value) = self.var(&i.to_id()) {
568 debug!("multi-replacer: Replaced `{}` as shorthand", i);
569 self.changed = true;
570
571 let key = prop_name_from_ident(i.take());
572 *p = Prop::KeyValue(KeyValueProp { key, value });
573 }
574 }
575 }
576
577 fn visit_mut_stmt(&mut self, node: &mut Stmt) {
578 if self.vars.is_empty() {
579 return;
580 }
581
582 node.visit_mut_children_with(self);
583 }
584}
585
586pub(crate) fn replace_id_with_expr<N>(node: &mut N, from: Id, to: Box<Expr>) -> Option<Box<Expr>>
587where
588 N: VisitMutWith<ExprReplacer>,
589{
590 let mut v = ExprReplacer { from, to: Some(to) };
591 node.visit_mut_with(&mut v);
592
593 v.to
594}
595
596pub(crate) struct ExprReplacer {
597 from: Id,
598 to: Option<Box<Expr>>,
599}
600
601impl ExprReplacer {
602 fn take(&mut self) -> Option<Box<Expr>> {
603 let e = self.to.take()?;
604
605 match &*e {
606 Expr::Ident(Ident { sym, .. }) if &**sym == "eval" => Some(
607 SeqExpr {
608 span: DUMMY_SP,
609 exprs: vec![0.into(), e],
610 }
611 .into(),
612 ),
613 _ => Some(e),
614 }
615 }
616}
617
618impl VisitMut for ExprReplacer {
619 noop_visit_mut_type!(fail);
620
621 fn visit_mut_expr(&mut self, e: &mut Expr) {
622 e.visit_mut_children_with(self);
623
624 if let Expr::Ident(i) = e {
625 if self.from.0 == i.sym && self.from.1 == i.ctxt {
626 if let Some(new) = self.take() {
627 *e = *new;
628 } else {
629 unreachable!("`{}` is already taken", i)
630 }
631 }
632 }
633 }
634
635 fn visit_mut_prop(&mut self, p: &mut Prop) {
636 p.visit_mut_children_with(self);
637
638 if let Prop::Shorthand(i) = p {
639 if self.from.0 == i.sym && self.from.1 == i.ctxt {
640 let value = if let Some(new) = self.take() {
641 new
642 } else {
643 unreachable!("`{}` is already taken", i)
644 };
645 let key = prop_name_from_ident(i.take());
646 *p = Prop::KeyValue(KeyValueProp { key, value });
647 }
648 }
649 }
650}
651
652#[derive(Debug, Default, PartialEq, Eq)]
653pub(super) struct SynthesizedStmts(Vec<Stmt>);
654
655impl SynthesizedStmts {
656 pub fn take_stmts(&mut self) -> Vec<Stmt> {
657 take(&mut self.0)
658 }
659}
660
661impl std::ops::Deref for SynthesizedStmts {
662 type Target = Vec<Stmt>;
663
664 fn deref(&self) -> &Self::Target {
665 &self.0
666 }
667}
668
669impl SynthesizedStmts {
670 pub fn push(&mut self, stmt: Stmt) {
671 self.0.push(stmt);
672 }
673
674 pub fn extend(&mut self, stmts: impl IntoIterator<Item = Stmt>) {
675 self.0.extend(stmts);
676 }
677
678 pub fn append(&mut self, other: &mut SynthesizedStmts) {
679 self.0.append(&mut other.0);
680 }
681}
682
683impl std::ops::DerefMut for SynthesizedStmts {
684 fn deref_mut(&mut self) -> &mut Self::Target {
685 &mut self.0
686 }
687}
688
689impl Take for SynthesizedStmts {
690 fn dummy() -> Self {
691 Self(Take::dummy())
692 }
693}
694
695impl Drop for SynthesizedStmts {
696 fn drop(&mut self) {
697 if !self.0.is_empty() {
698 if !std::thread::panicking() {
699 panic!("We should not drop synthesized stmts");
700 }
701 }
702 }
703}
704
705#[derive(Default)]
706struct LabelAnalyzer {
707 label: Atom,
708 top_breakable: bool,
710 count: usize,
711 break_layer: usize,
712 continue_layer: usize,
713}
714
715impl LabelAnalyzer {
716 fn visit_mut_loop(&mut self, n: &mut impl VisitMutWith<LabelAnalyzer>) {
717 self.break_layer += 1;
718 self.continue_layer += 1;
719
720 n.visit_mut_children_with(self);
721
722 self.break_layer -= 1;
723 self.continue_layer -= 1;
724 }
725}
726
727impl VisitMut for LabelAnalyzer {
728 fn visit_mut_function(&mut self, _: &mut Function) {}
729
730 fn visit_mut_class(&mut self, _: &mut Class) {}
731
732 fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
733
734 fn visit_mut_object_lit(&mut self, _: &mut ObjectLit) {}
735
736 fn visit_mut_for_stmt(&mut self, n: &mut ForStmt) {
737 self.visit_mut_loop(n)
738 }
739
740 fn visit_mut_for_in_stmt(&mut self, n: &mut ForInStmt) {
741 self.visit_mut_loop(n)
742 }
743
744 fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) {
745 self.visit_mut_loop(n)
746 }
747
748 fn visit_mut_while_stmt(&mut self, n: &mut WhileStmt) {
749 self.visit_mut_loop(n)
750 }
751
752 fn visit_mut_do_while_stmt(&mut self, n: &mut DoWhileStmt) {
753 self.visit_mut_loop(n)
754 }
755
756 fn visit_mut_switch_stmt(&mut self, n: &mut SwitchStmt) {
757 self.break_layer += 1;
758
759 n.visit_mut_children_with(self);
760
761 self.break_layer -= 1;
762 }
763
764 fn visit_mut_break_stmt(&mut self, n: &mut BreakStmt) {
765 if let Some(lb) = &n.label {
766 if lb.sym == self.label {
767 if self.break_layer > 0 || !self.top_breakable {
768 self.count += 1;
769 } else {
770 n.label = None
771 }
772 }
773 }
774 }
775
776 fn visit_mut_continue_stmt(&mut self, n: &mut ContinueStmt) {
777 if let Some(lb) = &n.label {
778 if lb.sym == self.label {
779 if self.continue_layer > 0 {
780 self.count += 1;
781 } else {
782 n.label = None
783 }
784 }
785 }
786 }
787}
788
789pub fn get_ids_of_pat(pat: &Pat) -> Vec<Id> {
790 fn append(pat: &Pat, ids: &mut Vec<Id>) {
791 match pat {
792 Pat::Ident(binding_ident) => ids.push(binding_ident.id.to_id()),
793 Pat::Array(array_pat) => {
794 for pat in array_pat.elems.iter().flatten() {
795 append(pat, ids);
796 }
797 }
798 Pat::Rest(rest_pat) => append(&rest_pat.arg, ids),
799 Pat::Object(object_pat) => {
800 for pat in &object_pat.props {
801 match pat {
802 ObjectPatProp::KeyValue(key_value_pat_prop) => {
803 append(&key_value_pat_prop.value, ids)
804 }
805 ObjectPatProp::Assign(assign_pat_prop) => {
806 ids.push(assign_pat_prop.key.to_id())
807 }
808 ObjectPatProp::Rest(rest_pat) => append(&rest_pat.arg, ids),
809 #[cfg(swc_ast_unknown)]
810 _ => panic!("unable to access unknown nodes"),
811 }
812 }
813 }
814 Pat::Assign(assign_pat) => append(&assign_pat.left, ids),
815 Pat::Invalid(_) | Pat::Expr(_) => {}
816 #[cfg(swc_ast_unknown)]
817 _ => panic!("unable to access unknown nodes"),
818 }
819 }
820
821 let mut idents = vec![];
822 append(pat, &mut idents);
823 idents
824}
825
826fn prop_name_from_ident(ident: Ident) -> PropName {
830 if ident.sym == "__proto__" {
831 PropName::Computed(ComputedPropName {
832 span: ident.span,
833 expr: Box::new(Expr::Lit(Lit::Str(Str {
834 span: ident.span,
835 value: ident.sym.clone().into(),
836 raw: None,
837 }))),
838 })
839 } else {
840 ident.into()
841 }
842}