swc_ecma_compat_es2015/
new_target.rs

1use 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                        // this instanceof Foo
76                        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                        // void 0
85                        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        // Ensure that `f` contains `new.target`.
95        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        // Ensure that `f` contains `new.target`.
108        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}