swc_ecma_compat_es2015/classes/
constructor.rs1use std::mem;
2
3use swc_common::{util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::{helper, helper_expr};
6use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
7use swc_ecma_utils::{default_constructor_with_span, private_ident, quote_ident, ExprFactory};
8use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
9use swc_trace_macro::swc_trace;
10use tracing::debug;
11
12use super::Config;
13
14pub(super) fn fold_constructor(
15 class_span: Span,
16 constructor: Option<Constructor>,
17 class_name: &Ident,
18 class_super_name: &Option<Ident>,
19 config: Config,
20) -> FnDecl {
21 let is_derived = class_super_name.is_some();
22 let mut constructor =
23 constructor.unwrap_or_else(|| default_constructor_with_span(is_derived, class_span));
24
25 let is_constructor_default = constructor.span.is_dummy();
27 if is_constructor_default {
28 debug!("Dropping constructor parameters because the constructor is injected");
29 constructor.params.take();
30 }
31
32 let params = constructor
33 .params
34 .take()
35 .into_iter()
36 .map(|p| p.param().expect("All TS params should be converted"))
37 .collect();
38
39 let mut stmts = vec![];
40
41 if !config.no_class_calls {
42 stmts.push(
44 CallExpr {
45 callee: helper!(class_call_check),
46 args: vec![
47 Expr::This(ThisExpr { span: DUMMY_SP }).as_arg(),
48 class_name.clone().as_arg(),
49 ],
50 ..Default::default()
51 }
52 .into_stmt(),
53 );
54 }
55
56 let mut has_super_prop = true;
58 let mut this_mark = None;
59
60 let mut body = constructor.body.take().unwrap();
61 if let Some(class_super_name) = class_super_name {
62 let is_last_super = (&*body.stmts).is_super_last_call();
63 let is_last_return = body.stmts.last().is_some_and(Stmt::is_return_stmt);
64
65 let mut constructor_folder = ConstructorFolder {
66 class_key_init: vec![],
67 class_name: class_name.clone(),
68 class_super_name: class_super_name.clone(),
69 in_arrow: false,
70 in_nested_class: false,
71 is_constructor_default,
72 is_super_callable_constructor: config.super_is_callable_constructor,
73 super_found: false,
74 super_prop_found: false,
75 this: None,
76 this_ref_count: 0,
77 };
78
79 body.visit_mut_with(&mut constructor_folder);
80
81 has_super_prop = constructor_folder.super_prop_found;
83
84 if is_last_super {
86 if let Some(stmt) = body.stmts.last_mut() {
87 if let Some(expr_stmt) = stmt.as_mut_expr() {
88 let span = expr_stmt.span;
89 match expr_stmt.expr.as_mut() {
90 Expr::Assign(assign) => {
91 let arg = if constructor_folder.this_ref_count == 1 {
92 constructor_folder.this_ref_count = 0;
93 assign.right.take()
94 } else {
95 assign.take().into()
96 };
97 *stmt = ReturnStmt {
98 span,
99 arg: arg.into(),
100 }
101 .into();
102 }
103 arg @ Expr::Seq(..) | arg @ Expr::Paren(..) => {
104 *stmt = ReturnStmt {
105 span,
106 arg: Some(Box::new(arg.take())),
107 }
108 .into();
109 }
110
111 _ => {}
112 }
113 };
114 }
115 }
116
117 if constructor_folder.this_ref_count > 0 || constructor_folder.super_prop_found {
118 let this = constructor_folder
119 .this
120 .get_or_insert_with(|| private_ident!("_this"))
121 .clone();
122
123 this_mark = Some(this.ctxt.outer());
124
125 let var = VarDeclarator {
126 name: Pat::Ident(this.into()),
127 span: DUMMY_SP,
128 init: None,
129 definite: false,
130 };
131
132 let var = VarDecl {
133 decls: vec![var],
134 ..Default::default()
135 };
136
137 stmts.push(var.into());
138 }
139
140 if !is_last_super && !is_last_return {
141 let this = if constructor_folder.super_found {
142 Expr::Ident(constructor_folder.this.unwrap())
143 } else {
144 let this = constructor_folder
145 .this
146 .map_or_else(|| Expr::undefined(DUMMY_SP).as_arg(), |this| this.as_arg());
147
148 helper_expr!(assert_this_initialized).as_call(DUMMY_SP, vec![this])
149 };
150
151 let return_this = ReturnStmt {
152 span: DUMMY_SP,
153 arg: Some(this.into()),
154 };
155 body.stmts.push(return_this.into());
156 }
157 }
158
159 if has_super_prop {
160 let mut folder = SuperFieldAccessFolder {
161 class_name,
162 constructor_this_mark: this_mark,
163 is_static: false,
165 folding_constructor: true,
166 in_nested_scope: false,
167 in_injected_define_property_call: false,
168 this_alias_mark: None,
169 constant_super: config.constant_super,
170 super_class: class_super_name,
171 in_pat: false,
172 };
173
174 body.visit_mut_with(&mut folder);
175
176 if let Some(mark) = folder.this_alias_mark {
177 stmts.push(
178 VarDecl {
179 span: DUMMY_SP,
180 declare: false,
181 kind: VarDeclKind::Var,
182 decls: vec![VarDeclarator {
183 span: DUMMY_SP,
184 name: quote_ident!(SyntaxContext::empty().apply_mark(mark), "_this").into(),
185 init: Some(Box::new(Expr::This(ThisExpr { span: DUMMY_SP }))),
186 definite: false,
187 }],
188 ..Default::default()
189 }
190 .into(),
191 )
192 }
193 }
194
195 stmts.extend(body.stmts);
196
197 let function = Function {
198 params,
199 body: Some(BlockStmt {
200 stmts,
201 ..Default::default()
202 }),
203 ..Default::default()
204 };
205
206 FnDecl {
207 ident: class_name.clone(),
208 declare: false,
209 function: function.into(),
210 }
211}
212
213struct ConstructorFolder {
214 class_key_init: Vec<Stmt>,
215 class_name: Ident,
216 class_super_name: Ident,
217
218 in_arrow: bool,
219 in_nested_class: bool,
220
221 is_constructor_default: bool,
222 is_super_callable_constructor: bool,
223
224 super_found: bool,
228 super_prop_found: bool,
229
230 this: Option<Ident>,
231 this_ref_count: usize,
232}
233
234#[swc_trace]
235impl VisitMut for ConstructorFolder {
236 noop_visit_mut_type!(fail);
237
238 fn visit_mut_constructor(&mut self, _: &mut Constructor) {
239 }
241
242 fn visit_mut_function(&mut self, _: &mut Function) {
243 }
245
246 fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {
247 }
249
250 fn visit_mut_setter_prop(&mut self, _: &mut SetterProp) {
251 }
253
254 fn visit_mut_if_stmt(&mut self, node: &mut IfStmt) {
255 node.test.visit_mut_with(self);
256 let super_found = self.super_found;
257 node.cons.visit_mut_with(self);
258 node.alt.visit_mut_with(self);
259 self.super_found = super_found;
260 }
261
262 fn visit_mut_while_stmt(&mut self, node: &mut WhileStmt) {
263 node.test.visit_mut_with(self);
264 let super_found = self.super_found;
265 node.body.visit_mut_with(self);
266 self.super_found = super_found;
267 }
268
269 fn visit_mut_do_while_stmt(&mut self, node: &mut DoWhileStmt) {
270 node.test.visit_mut_with(self);
271 let super_found = self.super_found;
272 node.body.visit_mut_with(self);
273 self.super_found = super_found;
274 }
275
276 fn visit_mut_for_stmt(&mut self, node: &mut ForStmt) {
277 node.init.visit_mut_with(self);
278 node.test.visit_mut_with(self);
279 let super_found = self.super_found;
280 node.body.visit_mut_with(self);
281 node.update.visit_mut_with(self);
282 self.super_found = super_found;
283 }
284
285 fn visit_mut_for_of_stmt(&mut self, node: &mut ForOfStmt) {
286 node.left.visit_mut_with(self);
287 node.right.visit_mut_with(self);
288 let super_found = self.super_found;
289 node.body.visit_mut_with(self);
290 self.super_found = super_found;
291 }
292
293 fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) {
294 node.left.visit_mut_with(self);
295 node.right.visit_mut_with(self);
296 let super_found = self.super_found;
297 node.body.visit_mut_with(self);
298 self.super_found = super_found;
299 }
300
301 fn visit_mut_cond_expr(&mut self, node: &mut CondExpr) {
302 node.test.visit_mut_with(self);
303 let super_found = self.super_found;
304 node.cons.visit_mut_with(self);
305 node.alt.visit_mut_with(self);
306 self.super_found = super_found;
307 }
308
309 fn visit_mut_switch_stmt(&mut self, node: &mut SwitchStmt) {
310 node.discriminant.visit_mut_with(self);
311 let super_found = self.super_found;
312 node.cases.visit_mut_with(self);
313 self.super_found = super_found;
314 }
315
316 fn visit_mut_try_stmt(&mut self, node: &mut TryStmt) {
317 let super_found = self.super_found;
318 node.block.visit_mut_with(self);
319 node.handler.visit_mut_with(self);
320 self.super_found = super_found;
321 node.finalizer.visit_mut_with(self);
322 }
323
324 fn visit_mut_labeled_stmt(&mut self, node: &mut LabeledStmt) {
325 if node.body.is_block() {
326 let super_found = self.super_found;
327 node.body.visit_mut_with(self);
328 self.super_found = super_found;
329 } else {
330 node.body.visit_mut_with(self);
331 }
332 }
333
334 fn visit_mut_bin_expr(&mut self, node: &mut BinExpr) {
335 match node.op {
336 op!("&&") | op!("||") => {
337 node.left.visit_mut_with(self);
338 let super_found = self.super_found;
339 node.right.visit_mut_with(self);
340 self.super_found = super_found;
341 }
342 _ => {
343 node.visit_mut_children_with(self);
344 }
345 }
346 }
347
348 fn visit_mut_class(&mut self, node: &mut Class) {
349 let in_nested_class = mem::replace(&mut self.in_nested_class, true);
350 node.visit_mut_children_with(self);
351 self.in_nested_class = in_nested_class;
352 }
353
354 fn visit_mut_arrow_expr(&mut self, node: &mut ArrowExpr) {
355 let in_arrow = mem::replace(&mut self.in_arrow, true);
356 let super_found = self.super_found;
357 node.visit_mut_children_with(self);
358 self.super_found = super_found;
359 self.in_arrow = in_arrow;
360 }
361
362 fn visit_mut_stmts(&mut self, node: &mut Vec<Stmt>) {
363 for mut stmt in node.take().drain(..) {
364 stmt.visit_mut_with(self);
365 let class_key_init = self.class_key_init.take();
366 if !class_key_init.is_empty() {
367 node.extend(class_key_init);
368 }
369 node.push(stmt);
370 }
371 }
372
373 fn visit_mut_expr(&mut self, node: &mut Expr) {
374 if node.is_this() {
375 if !self.super_found {
376 *node = helper_expr!(assert_this_initialized)
377 .as_call(DUMMY_SP, vec![self.get_this().clone().as_arg()]);
378 } else {
379 *node = self.get_this().clone().into();
380 }
381 return;
382 }
383
384 node.visit_mut_children_with(self);
385
386 if self.transform_super_call(node) {
387 self.super_found = true;
388
389 let this = self.get_this().clone();
390 let assign_expr = node.take().make_assign_to(op!("="), this.clone().into());
391
392 if self.in_nested_class {
393 self.class_key_init.push(assign_expr.into_stmt());
394 *node = this.into();
395 } else {
396 *node = assign_expr;
397 }
398 }
399 }
400
401 fn visit_mut_return_stmt(&mut self, node: &mut ReturnStmt) {
402 node.visit_mut_children_with(self);
403
404 if !self.in_arrow {
405 let arg = node.arg.take().map(ExprFactory::as_arg);
406 let mut args = vec![self.get_this().clone().as_arg()];
407 args.extend(arg);
408 node.arg = Some(
409 helper_expr!(possible_constructor_return)
410 .as_call(DUMMY_SP, args)
411 .into(),
412 );
413 }
414 }
415
416 fn visit_mut_super_prop(&mut self, node: &mut SuperProp) {
417 self.super_prop_found = true;
418
419 node.visit_mut_children_with(self);
420 }
421}
422
423#[swc_trace]
424impl ConstructorFolder {
425 fn get_this(&mut self) -> &Ident {
426 self.this_ref_count += 1;
427 self.this.get_or_insert_with(|| private_ident!("_this"))
428 }
429
430 fn transform_super_call(&self, node: &mut Expr) -> bool {
431 let Expr::Call(call_expr) = node else {
432 return false;
433 };
434
435 let CallExpr {
436 callee: callee @ Callee::Super(..),
437 args: origin_args,
438 ..
439 } = call_expr
440 else {
441 return false;
442 };
443
444 if self.is_super_callable_constructor {
445 if self.is_constructor_default || is_spread_arguements(origin_args) {
446 *callee = self
447 .class_super_name
448 .clone()
449 .make_member(quote_ident!("apply"))
450 .as_callee();
451
452 let mut arguments = quote_ident!("arguments");
453
454 if let Some(e) = origin_args.first() {
455 arguments.span = e.expr.span()
456 }
457
458 *origin_args = vec![ThisExpr { span: DUMMY_SP }.as_arg(), arguments.as_arg()];
459 } else {
460 *callee = self
461 .class_super_name
462 .clone()
463 .make_member(quote_ident!("call"))
464 .as_callee();
465 origin_args.insert(0, ThisExpr { span: DUMMY_SP }.as_arg());
466 }
467
468 *node = BinExpr {
469 span: DUMMY_SP,
470 left: Box::new(node.take()),
471 op: op!("||"),
472 right: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
473 }
474 .into();
475
476 return true;
477 }
478
479 *callee = helper!(call_super);
480
481 let mut args = vec![
482 ThisExpr { span: DUMMY_SP }.as_arg(),
483 self.class_name.clone().as_arg(),
484 ];
485
486 if self.is_constructor_default || is_spread_arguements(origin_args) {
487 let mut arguments = quote_ident!("arguments");
491
492 if let Some(e) = origin_args.first() {
493 arguments.span = e.expr.span()
494 }
495
496 args.push(arguments.as_arg())
497 } else if !origin_args.is_empty() {
498 let array = ArrayLit {
502 elems: origin_args.take().into_iter().map(Some).collect(),
503 ..Default::default()
504 };
505
506 args.push(array.as_arg());
507 }
508
509 *origin_args = args;
510
511 true
512 }
513}
514
515fn is_spread_arguements(args: &[ExprOrSpread]) -> bool {
517 if args.len() != 1 {
518 return false;
519 }
520
521 let arg = &args[0];
522
523 if arg.spread.is_none() {
524 return false;
525 }
526
527 arg.expr
528 .as_ident()
529 .filter(|ident| ident.sym == *"arguments")
530 .is_some()
531}
532
533trait SuperLastCall {
534 fn is_super_last_call(&self) -> bool;
535}
536
537impl SuperLastCall for &[Stmt] {
538 fn is_super_last_call(&self) -> bool {
539 self.iter()
540 .rev()
541 .find(|s| !s.is_empty())
542 .is_some_and(|s| s.is_super_last_call())
543 }
544}
545
546impl SuperLastCall for &Stmt {
547 fn is_super_last_call(&self) -> bool {
548 match self {
549 Stmt::Expr(ExprStmt { expr, .. }) => (&**expr).is_super_last_call(),
550 Stmt::Return(ReturnStmt { arg: Some(arg), .. }) => (&**arg).is_super_last_call(),
551 _ => false,
552 }
553 }
554}
555
556impl SuperLastCall for &Expr {
557 fn is_super_last_call(&self) -> bool {
558 match self {
559 Expr::Call(CallExpr {
560 callee: Callee::Super(..),
561 ..
562 }) => true,
563 Expr::Paren(ParenExpr { expr, .. }) => (&**expr).is_super_last_call(),
564 Expr::Seq(SeqExpr { exprs, .. }) => {
565 exprs.last().is_some_and(|e| (&**e).is_super_last_call())
566 }
567 _ => false,
568 }
569 }
570}