1use std::iter;
2
3use rustc_hash::FxHashMap;
4use swc_atoms::Atom;
5use swc_common::{errors::HANDLER, util::take::Take, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
6use swc_ecma_ast::*;
7use swc_ecma_transforms_base::helper;
8use swc_ecma_utils::{alias_ident_for, alias_if_required, prepend_stmt, quote_ident, ExprFactory};
9use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
10use swc_trace_macro::swc_trace;
11
12use super::Config;
13use crate::optional_chaining_impl::optional_chaining_impl;
14
15pub(super) struct Private {
16 pub mark: Mark,
17 pub class_name: Ident,
18 pub ident: FxHashMap<Atom, PrivateKind>,
19}
20
21pub(super) struct PrivateRecord(Vec<Private>);
22
23#[swc_trace]
24impl PrivateRecord {
25 pub fn new() -> Self {
26 PrivateRecord(Vec::new())
27 }
28
29 pub fn curr_class(&self) -> &Ident {
30 &self.0.last().unwrap().class_name
31 }
32
33 pub fn cur_mark(&self) -> Mark {
34 self.0.last().unwrap().mark
35 }
36
37 pub fn push(&mut self, p: Private) {
38 self.0.push(p)
39 }
40
41 pub fn pop(&mut self) {
42 self.0.pop();
43 }
44
45 pub fn get(&self, span: Span, name: &Atom) -> (Mark, PrivateKind, &Ident) {
46 for p in self.0.iter().rev() {
47 if let Some(kind) = p.ident.get(name) {
48 return (p.mark, *kind, &p.class_name);
49 }
50 }
51
52 let error = format!("private name #{name} is not defined.");
53 HANDLER.with(|handler| handler.struct_span_err(span, &error).emit());
54 (Mark::root(), PrivateKind::default(), &self.0[0].class_name)
55 }
56}
57
58#[derive(Copy, Clone, PartialEq, Default, Eq)]
59pub(super) struct PrivateKind {
60 pub is_static: bool,
61 pub is_method: bool,
62 pub has_getter: bool,
63 pub has_setter: bool,
64}
65
66impl PrivateKind {
67 fn is_readonly(&self) -> bool {
68 self.is_method && !self.has_setter
69 }
70
71 fn is_writeonly(&self) -> bool {
72 self.is_method && !self.has_getter && self.has_setter
74 }
75
76 fn is_method(&self) -> bool {
77 self.is_method && !self.has_getter && !self.has_setter
78 }
79}
80
81pub(super) struct BrandCheckHandler<'a> {
82 pub private: &'a PrivateRecord,
83}
84
85#[swc_trace]
86impl VisitMut for BrandCheckHandler<'_> {
87 noop_visit_mut_type!(fail);
88
89 fn visit_mut_expr(&mut self, e: &mut Expr) {
90 e.visit_mut_children_with(self);
91
92 match e {
93 Expr::Bin(BinExpr {
94 span,
95 op: op!("in"),
96 left,
97 right,
98 }) if left.is_private_name() => {
99 let n = left.as_private_name().unwrap();
100 if let Expr::Ident(right) = &**right {
101 let curr_class = self.private.curr_class();
102 if curr_class.sym == right.sym && curr_class.ctxt == right.ctxt {
103 *e = BinExpr {
104 span: *span,
105 op: op!("==="),
106 left: curr_class.clone().into(),
107 right: right.clone().into(),
108 }
109 .into();
110 return;
111 }
112 }
113
114 let (mark, kind, class_name) = self.private.get(n.span, &n.name);
115
116 if mark == Mark::root() {
117 return;
118 }
119
120 if kind.is_static {
121 *e = BinExpr {
122 span: *span,
123 op: op!("==="),
124 left: right.take(),
125 right: class_name.clone().into(),
126 }
127 .into();
128 return;
129 }
130
131 let weak_coll_ident = Ident::new(
132 format!("_{}", n.name).into(),
133 n.span,
134 SyntaxContext::empty().apply_mark(mark),
135 );
136
137 *e = CallExpr {
138 span: *span,
139 callee: weak_coll_ident.make_member(quote_ident!("has")).as_callee(),
140 args: vec![right.take().as_arg()],
141
142 ..Default::default()
143 }
144 .into();
145 }
146
147 _ => {}
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
153pub(super) enum PrivateAccessType {
154 Get,
155 DestructureSet,
156 Update,
157}
158
159impl Default for PrivateAccessType {
160 fn default() -> Self {
161 Self::Get
162 }
163}
164
165pub(super) struct PrivateAccessVisitor<'a> {
166 pub vars: Vec<VarDeclarator>,
167 pub private: &'a PrivateRecord,
168 pub private_access_type: PrivateAccessType,
169 pub c: Config,
170 pub unresolved_mark: Mark,
171}
172
173macro_rules! take_vars {
174 ($name:ident, $T:tt) => {
175 fn $name(&mut self, f: &mut $T) {
176 let old_var = self.vars.take();
177
178 if f.body.is_none() {
179 return;
180 }
181
182 f.visit_mut_children_with(self);
183
184 if !self.vars.is_empty() {
185 prepend_stmt(
186 &mut f.body.as_mut().unwrap().stmts,
187 VarDecl {
188 span: DUMMY_SP,
189 kind: VarDeclKind::Var,
190 decls: self.vars.take(),
191
192 ..Default::default()
193 }
194 .into(),
195 )
196 }
197
198 self.vars = old_var;
199 }
200 };
201}
202
203#[swc_trace]
205impl VisitMut for PrivateAccessVisitor<'_> {
206 noop_visit_mut_type!(fail);
207
208 take_vars!(visit_mut_function, Function);
209
210 take_vars!(visit_mut_constructor, Constructor);
211
212 fn visit_mut_expr(&mut self, e: &mut Expr) {
213 if let Expr::OptChain(opt) = e {
214 let is_private_access = match &*opt.base {
215 OptChainBase::Member(MemberExpr {
216 prop: MemberProp::PrivateName(..),
217 ..
218 }) => true,
219 OptChainBase::Call(OptCall { callee, .. }) => matches!(
220 &**callee,
221 Expr::Member(MemberExpr {
222 prop: MemberProp::PrivateName(..),
223 ..
224 })
225 ),
226 _ => false,
227 };
228
229 if is_private_access {
230 let mut v = optional_chaining_impl(
231 crate::optional_chaining_impl::Config {
232 no_document_all: self.c.no_document_all,
233 pure_getter: self.c.pure_getter,
234 },
235 self.unresolved_mark,
236 );
237 e.visit_mut_with(&mut v);
238 assert!(!e.is_opt_chain(), "optional chaining should be removed");
239 self.vars.extend(v.take_vars());
240 }
241 }
242
243 if self.c.private_as_properties {
244 if let Expr::Member(MemberExpr {
245 span,
246 obj,
247 prop: MemberProp::PrivateName(n),
248 }) = e
249 {
250 obj.visit_mut_children_with(self);
251 let (mark, _, _) = self.private.get(n.span, &n.name);
252 let ident = Ident::new(
253 format!("_{}", n.name).into(),
254 n.span,
255 SyntaxContext::empty().apply_mark(mark),
256 );
257
258 *e = CallExpr {
259 callee: helper!(class_private_field_loose_base),
260 span: *span,
261 args: vec![obj.take().as_arg(), ident.clone().as_arg()],
262 ..Default::default()
263 }
264 .computed_member(ident)
265 .into();
266 } else {
267 e.visit_mut_children_with(self)
268 }
269 return;
270 }
271
272 match e {
273 Expr::Update(UpdateExpr { arg, .. }) if arg.is_member() => {
274 let old_access_type = self.private_access_type;
275
276 self.private_access_type = PrivateAccessType::Update;
277 arg.visit_mut_with(self);
278 self.private_access_type = old_access_type;
279 }
280
281 Expr::Assign(AssignExpr {
282 span,
283 left,
284 op,
285 right,
286 }) if left.as_simple().is_some() && left.as_simple().unwrap().is_member() => {
287 let mut left: MemberExpr = left.take().expect_simple().expect_member();
288 left.visit_mut_with(self);
289 right.visit_mut_with(self);
290
291 let n = match &left.prop {
292 MemberProp::PrivateName(n) => n.clone(),
293 _ => {
294 *e = AssignExpr {
295 span: *span,
296 left: left.into(),
297 op: *op,
298 right: right.take(),
299 }
300 .into();
301
302 return;
303 }
304 };
305
306 let obj = left.obj.clone();
307
308 let (mark, kind, class_name) = self.private.get(n.span, &n.name);
309 if mark == Mark::root() {
310 return;
311 }
312
313 let ident = Ident::new(
314 format!("_{}", n.name).into(),
315 n.span,
316 SyntaxContext::empty().apply_mark(mark),
317 );
318
319 let var = alias_ident_for(&obj, "_ref");
320
321 let this = if matches!(*obj, Expr::This(..)) {
322 Box::new(ThisExpr { span: DUMMY_SP }.into())
323 } else if *op == op!("=") {
324 obj
325 } else {
326 self.vars.push(VarDeclarator {
327 span: DUMMY_SP,
328 name: var.clone().into(),
329 init: None,
330 definite: false,
331 });
332 Box::new(
333 AssignExpr {
334 span: obj.span(),
335 left: var.clone().into(),
336 op: op!("="),
337 right: obj,
338 }
339 .into(),
340 )
341 };
342
343 let value = if *op == op!("=") {
344 right.take()
345 } else {
346 let left = Box::new(self.visit_mut_private_get(&mut left, Some(var)).0);
347
348 Box::new(
349 BinExpr {
350 span: DUMMY_SP,
351 left,
352 op: op.to_update().unwrap(),
353 right: right.take(),
354 }
355 .into(),
356 )
357 };
358
359 if kind.is_static {
360 *e = CallExpr {
361 span: DUMMY_SP,
362 callee: helper!(class_static_private_field_spec_set),
363 args: vec![
364 this.as_arg(),
365 class_name.clone().as_arg(),
366 ident.as_arg(),
367 value.as_arg(),
368 ],
369
370 ..Default::default()
371 }
372 .into();
373 } else if kind.is_readonly() {
374 let err = CallExpr {
375 span: DUMMY_SP,
376 callee: helper!(read_only_error),
377 args: vec![format!("#{}", n.name).as_arg()],
378 ..Default::default()
379 }
380 .into();
381 *e = SeqExpr {
382 span: *span,
383 exprs: vec![this, value, err],
384 }
385 .into();
386 } else {
387 let set = helper!(class_private_field_set);
388
389 *e = CallExpr {
390 span: DUMMY_SP,
391 callee: set,
392 args: vec![this.as_arg(), ident.as_arg(), value.as_arg()],
393
394 ..Default::default()
395 }
396 .into();
397 }
398 }
399
400 Expr::Assign(AssignExpr {
401 left: AssignTarget::Pat(left),
402
403 right,
404 ..
405 }) => {
406 right.visit_mut_with(self);
407 let old_access_type = self.private_access_type;
408
409 self.private_access_type = PrivateAccessType::DestructureSet;
410 left.visit_mut_with(self);
411 self.private_access_type = old_access_type;
412 }
413
414 Expr::TaggedTpl(TaggedTpl { span, tag, tpl, .. }) if tag.is_member() => {
416 let mut tag = tag.take().member().unwrap();
417 tag.visit_mut_with(self);
418 tpl.visit_mut_with(self);
419
420 let (expr, this) = self.visit_mut_private_get(&mut tag, None);
421
422 if let Some(this) = this {
423 *e = TaggedTpl {
424 span: *span,
425 tag: CallExpr {
426 span: DUMMY_SP,
427 callee: expr.make_member(quote_ident!("bind")).as_callee(),
428 args: vec![this.as_arg()],
429 ..Default::default()
430 }
431 .into(),
432 tpl: tpl.take(),
433 ..Default::default()
434 }
435 .into();
436 } else {
437 *e = TaggedTpl {
438 span: *span,
439 tag: Box::new(expr),
440 tpl: tpl.take(),
441 ..Default::default()
442 }
443 .into();
444 }
445 }
446
447 Expr::Call(CallExpr {
448 span,
449 callee: Callee::Expr(callee),
450 args,
451 ..
452 }) if callee.is_member() => {
453 let mut callee = callee.take().member().unwrap();
454 callee.visit_mut_with(self);
455 args.visit_mut_with(self);
456
457 let (expr, this) = self.visit_mut_private_get(&mut callee, None);
458 if let Some(this) = this {
459 *e = CallExpr {
460 span: *span,
461 callee: expr.make_member(quote_ident!("call")).as_callee(),
462 args: iter::once(this.as_arg()).chain(args.take()).collect(),
463 ..Default::default()
464 }
465 .into();
466 } else {
467 *e = CallExpr {
468 span: *span,
469 callee: expr.as_callee(),
470 args: args.take(),
471 ..Default::default()
472 }
473 .into();
474 }
475 }
476
477 Expr::Member(member_expr) => {
478 member_expr.visit_mut_children_with(self);
479 *e = self.visit_mut_private_get(member_expr, None).0;
480 }
481
482 _ => e.visit_mut_children_with(self),
483 };
484 }
485
486 fn visit_mut_simple_assign_target(&mut self, e: &mut SimpleAssignTarget) {
487 if let SimpleAssignTarget::OptChain(opt) = e {
488 let is_private_access = match &*opt.base {
489 OptChainBase::Member(MemberExpr {
490 prop: MemberProp::PrivateName(..),
491 ..
492 }) => true,
493 OptChainBase::Call(OptCall { callee, .. }) => matches!(
494 &**callee,
495 Expr::Member(MemberExpr {
496 prop: MemberProp::PrivateName(..),
497 ..
498 })
499 ),
500 _ => false,
501 };
502
503 if is_private_access {
504 let mut v = optional_chaining_impl(
505 crate::optional_chaining_impl::Config {
506 no_document_all: self.c.no_document_all,
507 pure_getter: self.c.pure_getter,
508 },
509 self.unresolved_mark,
510 );
511 e.visit_mut_with(&mut v);
512 assert!(!e.is_opt_chain(), "optional chaining should be removed");
513 self.vars.extend(v.take_vars());
514 }
515 }
516
517 if self.c.private_as_properties {
518 if let SimpleAssignTarget::Member(MemberExpr {
519 span,
520 obj,
521 prop: MemberProp::PrivateName(n),
522 }) = e
523 {
524 obj.visit_mut_children_with(self);
525 let (mark, _, _) = self.private.get(n.span, &n.name);
526 let ident = Ident::new(
527 format!("_{}", n.name).into(),
528 n.span,
529 SyntaxContext::empty().apply_mark(mark),
530 );
531
532 *e = CallExpr {
533 callee: helper!(class_private_field_loose_base),
534 span: *span,
535 args: vec![obj.take().as_arg(), ident.clone().as_arg()],
536 ..Default::default()
537 }
538 .computed_member(ident)
539 .into();
540 } else {
541 e.visit_mut_children_with(self)
542 }
543 return;
544 }
545
546 e.visit_mut_children_with(self)
547 }
548
549 fn visit_mut_pat(&mut self, p: &mut Pat) {
550 if let Pat::Expr(expr) = &p {
551 if let Expr::Member(me) = &**expr {
552 if let MemberProp::PrivateName(..) = &me.prop {
553 let old_access_type = self.private_access_type;
554 self.private_access_type = PrivateAccessType::DestructureSet;
555 p.visit_mut_children_with(self);
556 self.private_access_type = old_access_type;
557
558 return;
559 }
560 }
561 }
562
563 self.private_access_type = Default::default();
564 p.visit_mut_children_with(self);
565 }
566}
567
568pub(super) fn visit_private_in_expr(
569 expr: &mut Expr,
570 private: &PrivateRecord,
571 config: Config,
572 unresolved_mark: Mark,
573) -> Vec<VarDeclarator> {
574 let mut priv_visitor = PrivateAccessVisitor {
575 private,
576 vars: Vec::new(),
577 private_access_type: Default::default(),
578 c: config,
579 unresolved_mark,
580 };
581
582 expr.visit_mut_with(&mut priv_visitor);
583
584 priv_visitor.vars
585}
586
587#[swc_trace]
588impl PrivateAccessVisitor<'_> {
589 fn visit_mut_private_get(
594 &mut self,
595 e: &mut MemberExpr,
596 obj_alias: Option<Ident>,
597 ) -> (Expr, Option<Expr>) {
598 let is_alias_initialized = obj_alias.is_some();
599
600 let n = match &e.prop {
601 MemberProp::PrivateName(n) => n,
602 _ => return (e.take().into(), None),
603 };
604
605 let mut obj = e.obj.take();
606
607 let (mark, kind, class_name) = self.private.get(n.span, &n.name);
608 if mark == Mark::root() {
609 return (Expr::dummy(), None);
610 }
611
612 let method_name = Ident::new(
613 if n.name.is_reserved_in_any() {
614 format!("__{}", n.name).into()
615 } else {
616 n.name.clone()
617 },
618 n.span,
619 SyntaxContext::empty().apply_mark(mark),
620 );
621 let ident = Ident::new(
622 format!("_{}", n.name).into(),
623 n.span,
624 SyntaxContext::empty().apply_mark(mark),
625 );
626
627 if kind.is_static {
628 match self.private_access_type {
629 PrivateAccessType::DestructureSet => {
630 let set = helper!(class_static_private_field_destructure);
631
632 return (
633 CallExpr {
634 span: DUMMY_SP,
635 callee: set,
636 args: vec![
637 obj.clone().as_arg(),
638 class_name.clone().as_arg(),
639 ident.as_arg(),
640 ],
641 ..Default::default()
642 }
643 .make_member(quote_ident!("value"))
644 .into(),
645 Some(*obj),
646 );
647 }
648 PrivateAccessType::Update => {
649 let set = helper!(class_static_private_field_update);
650
651 return (
652 CallExpr {
653 span: DUMMY_SP,
654 callee: set,
655 args: vec![
656 obj.clone().as_arg(),
657 class_name.clone().as_arg(),
658 ident.as_arg(),
659 ],
660
661 ..Default::default()
662 }
663 .make_member(quote_ident!("value"))
664 .into(),
665 Some(*obj),
666 );
667 }
668 _ => {}
669 }
670
671 if kind.is_method() {
672 let h = helper!(class_static_private_method_get);
673
674 return (
675 CallExpr {
676 span: DUMMY_SP,
677 callee: h,
678 args: vec![
679 obj.as_arg(),
680 class_name.clone().as_arg(),
681 method_name.as_arg(),
682 ],
683 ..Default::default()
684 }
685 .into(),
686 Some(class_name.clone().into()),
687 );
688 }
689
690 let get = helper!(class_static_private_field_spec_get);
691
692 (
693 CallExpr {
694 span: DUMMY_SP,
695 callee: get,
696 args: vec![obj.as_arg(), class_name.clone().as_arg(), ident.as_arg()],
697 ..Default::default()
698 }
699 .into(),
700 Some(class_name.clone().into()),
701 )
702 } else {
703 match self.private_access_type {
704 PrivateAccessType::DestructureSet => {
705 let set = helper!(class_private_field_destructure);
706
707 (
708 CallExpr {
709 span: DUMMY_SP,
710 callee: set,
711 args: vec![obj.clone().as_arg(), ident.as_arg()],
712
713 ..Default::default()
714 }
715 .make_member(quote_ident!("value"))
716 .into(),
717 Some(*obj),
718 )
719 }
720 PrivateAccessType::Update => {
721 let set = helper!(class_private_field_update);
722
723 (
724 CallExpr {
725 span: DUMMY_SP,
726 callee: set,
727 args: vec![obj.clone().as_arg(), ident.as_arg()],
728
729 ..Default::default()
730 }
731 .make_member(quote_ident!("value"))
732 .into(),
733 Some(*obj),
734 )
735 }
736
737 PrivateAccessType::Get if kind.is_writeonly() => {
738 let helper = helper!(write_only_error);
739 let expr = Box::new(
740 CallExpr {
741 span: DUMMY_SP,
742 callee: helper,
743 args: vec![format!("#{}", n.name).as_arg()],
744 ..Default::default()
745 }
746 .into(),
747 );
748 (
749 SeqExpr {
750 span: DUMMY_SP,
751 exprs: vec![obj.clone(), expr],
752 }
753 .into(),
754 Some(*obj),
755 )
756 }
757
758 PrivateAccessType::Get => {
759 let get = if self.c.private_as_properties {
760 helper!(class_private_field_loose_base)
761 } else if kind.is_method() {
762 helper!(class_private_method_get)
763 } else {
764 helper!(class_private_field_get)
765 };
766
767 match &*obj {
768 Expr::This(this) => (
769 if kind.is_method() && !self.c.private_as_properties {
770 CallExpr {
771 span: DUMMY_SP,
772 callee: get,
773 args: vec![
774 obj.clone().as_arg(),
775 ident.as_arg(),
776 method_name.as_arg(),
777 ],
778 ..Default::default()
779 }
780 .into()
781 } else {
782 CallExpr {
783 span: DUMMY_SP,
784 callee: get,
785 args: vec![this.as_arg(), ident.as_arg()],
786 ..Default::default()
787 }
788 .into()
789 },
790 Some(Expr::This(*this)),
791 ),
792 _ => {
793 let mut aliased = false;
794 let var = obj_alias.unwrap_or_else(|| {
795 let (var, a) = alias_if_required(&obj, "_ref");
796 if a {
797 aliased = true;
798 self.vars.push(VarDeclarator {
799 span: DUMMY_SP,
800 name: var.clone().into(),
801 init: None,
802 definite: false,
803 });
804 }
805 var
806 });
807
808 let first_arg = if is_alias_initialized {
809 var.clone().as_arg()
810 } else if aliased {
811 AssignExpr {
812 span: DUMMY_SP,
813 left: var.clone().into(),
814 op: op!("="),
815 right: obj.take(),
816 }
817 .as_arg()
818 } else {
819 var.clone().as_arg()
820 };
821
822 let args = if kind.is_method() {
823 vec![first_arg, ident.as_arg(), method_name.as_arg()]
824 } else {
825 vec![first_arg, ident.as_arg()]
826 };
827
828 (
829 CallExpr {
830 span: DUMMY_SP,
831 callee: get,
832 args,
833 ..Default::default()
834 }
835 .into(),
836 Some(var.into()),
837 )
838 }
839 }
840 }
841 }
842 }
843 }
844}
845
846pub(super) fn dup_private_method(kind: &PrivateKind, method: &PrivateMethod) -> bool {
848 if !kind.is_method || kind.is_static != method.is_static || method.kind == MethodKind::Method {
849 return true;
850 }
851
852 !matches!(
853 (method.kind, kind.has_getter, kind.has_setter),
854 (MethodKind::Getter, false, true) | (MethodKind::Setter, true, false)
855 )
856}