swc_ecma_compat_es2015/
new_target.rs1use std::{borrow::Cow, mem};
2
3use swc_common::{pass::CompilerPass, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::perf::{should_work, Check};
6use swc_ecma_utils::{private_ident, quote_ident, ExprFactory};
7use swc_ecma_visit::{
8 noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith,
9};
10use swc_trace_macro::swc_trace;
11
12pub fn new_target() -> impl Pass {
13 visit_mut_pass(NewTarget {
14 ctx: Ctx::Constructor,
15 })
16}
17
18struct NewTarget {
19 ctx: Ctx,
20}
21
22enum Ctx {
23 Constructor,
24 Method,
25 Function(Ident),
26}
27
28impl NewTarget {
29 fn visit_mut_method<T: VisitMutWith<Self>>(&mut self, c: &mut T) {
30 let old = mem::replace(&mut self.ctx, Ctx::Method);
31
32 c.visit_mut_with(self);
33
34 self.ctx = old;
35 }
36}
37
38#[swc_trace]
39impl VisitMut for NewTarget {
40 noop_visit_mut_type!(fail);
41
42 fn visit_mut_class_method(&mut self, c: &mut ClassMethod) {
43 c.key.visit_mut_with(self);
44
45 self.visit_mut_method(&mut c.function)
46 }
47
48 fn visit_mut_constructor(&mut self, c: &mut Constructor) {
49 let old = mem::replace(&mut self.ctx, Ctx::Constructor);
50
51 c.visit_mut_children_with(self);
52
53 self.ctx = old;
54 }
55
56 fn visit_mut_expr(&mut self, e: &mut Expr) {
57 e.visit_mut_children_with(self);
58
59 if let Expr::MetaProp(MetaPropExpr {
60 kind: MetaPropKind::NewTarget,
61 span,
62 }) = e
63 {
64 let this_ctor = |span| {
65 ThisExpr { span }
66 .make_member(quote_ident!("constructor"))
67 .into()
68 };
69 match &self.ctx {
70 Ctx::Constructor => *e = this_ctor(*span),
71 Ctx::Method => *e = *Expr::undefined(DUMMY_SP),
72 Ctx::Function(i) => {
73 *e = CondExpr {
74 span: *span,
75 test: BinExpr {
77 span: DUMMY_SP,
78 op: op!("instanceof"),
79 left: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
80 right: Box::new(Expr::Ident(i.clone())),
81 }
82 .into(),
83 cons: Box::new(this_ctor(DUMMY_SP)),
84 alt: Expr::undefined(DUMMY_SP),
86 }
87 .into()
88 }
89 }
90 }
91 }
92
93 fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
94 if !should_work::<ShouldWork, _>(&*f) {
96 return;
97 }
98
99 let old = mem::replace(&mut self.ctx, Ctx::Function(f.ident.clone()));
100
101 f.visit_mut_children_with(self);
102
103 self.ctx = old;
104 }
105
106 fn visit_mut_fn_expr(&mut self, f: &mut FnExpr) {
107 if !should_work::<ShouldWork, _>(&*f) {
109 return;
110 }
111
112 let i = f
113 .ident
114 .get_or_insert_with(|| private_ident!("_target"))
115 .clone();
116
117 let old = mem::replace(&mut self.ctx, Ctx::Function(i));
118
119 f.visit_mut_children_with(self);
120
121 self.ctx = old;
122 }
123
124 fn visit_mut_method_prop(&mut self, m: &mut MethodProp) {
125 m.key.visit_mut_with(self);
126 self.visit_mut_method(&mut m.function)
127 }
128
129 fn visit_mut_getter_prop(&mut self, m: &mut GetterProp) {
130 m.key.visit_mut_with(self);
131 self.visit_mut_method(&mut m.body)
132 }
133
134 fn visit_mut_setter_prop(&mut self, m: &mut SetterProp) {
135 m.key.visit_mut_with(self);
136 self.visit_mut_method(&mut m.body)
137 }
138}
139
140impl CompilerPass for NewTarget {
141 fn name(&self) -> Cow<'static, str> {
142 Cow::Borrowed("new-target")
143 }
144}
145
146#[derive(Default)]
147struct ShouldWork {
148 found: bool,
149}
150
151impl Visit for ShouldWork {
152 noop_visit_type!(fail);
153
154 fn visit_meta_prop_expr(&mut self, n: &MetaPropExpr) {
155 if let MetaPropExpr {
156 kind: MetaPropKind::NewTarget,
157 ..
158 } = n
159 {
160 self.found = true;
161 }
162 }
163}
164
165impl Check for ShouldWork {
166 fn should_handle(&self) -> bool {
167 self.found
168 }
169}