swc_ecma_utils/function/
function_wrapper.rs

1use std::marker::PhantomData;
2
3use swc_common::{util::take::Take, Spanned, DUMMY_SP};
4use swc_ecma_ast::*;
5
6use crate::ExprFactory;
7
8pub struct FunctionWrapper<T> {
9    pub binding_ident: Option<Ident>,
10    pub function: Expr,
11
12    pub ignore_function_name: bool,
13    pub ignore_function_length: bool,
14
15    function_ident: Option<Ident>,
16    params: Vec<Param>,
17
18    _type: PhantomData<T>,
19}
20
21impl<T> FunctionWrapper<T> {
22    /// `get_params` clone only the parameters that count in function length.
23    fn get_params<'a, ParamsIter, Item>(params_iter: ParamsIter) -> Vec<Param>
24    where
25        Item: Into<&'a Param>,
26        ParamsIter: IntoIterator<Item = Item>,
27    {
28        params_iter
29            .into_iter()
30            .map(Into::into)
31            .map_while(|param| match param.pat {
32                Pat::Ident(..) => Some(param.clone()),
33                Pat::Array(..) | Pat::Object(..) => Some(Param {
34                    span: param.span,
35                    decorators: param.decorators.clone(),
36                    pat: private_ident!("_").into(),
37                }),
38                _ => None,
39            })
40            .collect()
41    }
42
43    ///
44    /// ```javascript
45    /// (function () {
46    ///     var REF = FUNCTION;
47    ///     return function NAME(PARAMS) {
48    ///         return REF.apply(this, arguments);
49    ///     };
50    /// })()
51    /// ```
52    fn build_anonymous_expression_wrapper(&mut self) -> Expr {
53        let name_ident = self.binding_ident.take();
54        let ref_ident = private_ident!("_ref");
55
56        let ref_decl: Decl = VarDecl {
57            kind: VarDeclKind::Var,
58            decls: vec![VarDeclarator {
59                span: DUMMY_SP,
60                name: Pat::Ident(ref_ident.clone().into()),
61                init: Some(Box::new(self.function.take())),
62                definite: false,
63            }],
64            ..Default::default()
65        }
66        .into();
67
68        let return_fn_stmt = {
69            let fn_expr = self.build_function_forward(ref_ident, name_ident);
70
71            ReturnStmt {
72                span: DUMMY_SP,
73                arg: Some(Box::new(fn_expr.into())),
74            }
75        }
76        .into();
77
78        let block_stmt = BlockStmt {
79            stmts: vec![ref_decl.into(), return_fn_stmt],
80            ..Default::default()
81        };
82
83        let function = Box::new(Function {
84            span: DUMMY_SP,
85            body: Some(block_stmt),
86            is_generator: false,
87            is_async: false,
88            ..Default::default()
89        });
90
91        FnExpr {
92            ident: None,
93            function,
94        }
95        .as_iife()
96        .into()
97    }
98
99    ///
100    /// ```javascript
101    /// (function () {
102    ///     var REF = FUNCTION;
103    ///     function NAME(PARAMS) {
104    ///         return REF.apply(this, arguments);
105    ///     }
106    ///     return NAME;
107    /// })()
108    /// ```
109    fn build_named_expression_wrapper(&mut self, name_ident: Ident) -> Expr {
110        let ref_ident = self.function_ident.as_ref().map_or_else(
111            || private_ident!("_ref"),
112            |ident| private_ident!(ident.span, format!("_{}", ident.sym)),
113        );
114
115        let ref_stmt: Stmt = VarDecl {
116            kind: VarDeclKind::Var,
117            decls: vec![VarDeclarator {
118                span: DUMMY_SP,
119                name: Pat::Ident(ref_ident.clone().into()),
120                init: Some(Box::new(self.function.take())),
121                definite: false,
122            }],
123
124            ..Default::default()
125        }
126        .into();
127
128        let fn_decl_stmt = {
129            let FnExpr { function, .. } = self.build_function_forward(ref_ident, None);
130
131            FnDecl {
132                ident: name_ident.clone(),
133                declare: false,
134                function,
135            }
136            .into()
137        };
138
139        let return_stmt = ReturnStmt {
140            span: DUMMY_SP,
141            arg: Some(Box::new(name_ident.into())),
142        }
143        .into();
144
145        let block_stmt = BlockStmt {
146            stmts: vec![ref_stmt, fn_decl_stmt, return_stmt],
147            ..Default::default()
148        };
149
150        let function = Box::new(Function {
151            span: DUMMY_SP,
152            body: Some(block_stmt),
153            is_generator: false,
154            is_async: false,
155            ..Default::default()
156        });
157
158        FnExpr {
159            ident: None,
160            function,
161        }
162        .as_iife()
163        .into()
164    }
165
166    ///
167    /// ```javascript
168    /// function NAME(PARAMS) {
169    ///     return REF.apply(this, arguments);
170    /// }
171    /// function REF() {
172    ///     REF = FUNCTION;
173    ///     return REF.apply(this, arguments);
174    /// }
175    /// ```
176    fn build_declaration_wrapper(&mut self, name_ident: Option<Ident>) -> (FnExpr, FnDecl) {
177        let ref_ident = self.function_ident.as_ref().map_or_else(
178            || private_ident!("_ref"),
179            |ident| private_ident!(ident.span, format!("_{}", ident.sym)),
180        );
181
182        // function NAME
183        let fn_expr = self.build_function_forward(ref_ident.clone(), name_ident);
184
185        let assign_stmt = AssignExpr {
186            span: DUMMY_SP,
187            op: op!("="),
188            left: ref_ident.clone().into(),
189            right: Box::new(self.function.take()),
190        }
191        .into_stmt();
192
193        // clone `return REF.apply(this, arguments);`
194        let return_ref_apply_stmt = fn_expr
195            .function
196            .body
197            .as_ref()
198            .expect("The `fn_expr` we construct cannot be None")
199            .stmts[0]
200            .clone();
201
202        let ref_fn_block_stmt = BlockStmt {
203            stmts: vec![assign_stmt, return_ref_apply_stmt],
204            ..Default::default()
205        };
206
207        // function REF
208        let ref_decl = FnDecl {
209            declare: false,
210            ident: ref_ident,
211            function: Box::new(Function {
212                span: DUMMY_SP,
213                is_async: false,
214                is_generator: false,
215                params: self.params.take(),
216                body: Some(ref_fn_block_stmt),
217                ..Default::default()
218            }),
219        };
220
221        (fn_expr, ref_decl)
222    }
223
224    ///
225    /// ```javascript
226    /// function NAME(PARAMS) {
227    ///     return REF.apply(this, arguments);
228    /// }
229    /// ```
230    fn build_function_forward(&mut self, ref_ident: Ident, name_ident: Option<Ident>) -> FnExpr {
231        let apply = ReturnStmt {
232            span: DUMMY_SP,
233            arg: Some(Box::new(ref_ident.apply(
234                DUMMY_SP,
235                ThisExpr { span: DUMMY_SP }.into(),
236                vec![quote_ident!("arguments").as_arg()],
237            ))),
238        }
239        .into();
240
241        FnExpr {
242            ident: name_ident,
243            function: Box::new(Function {
244                params: self.params.take(),
245                span: DUMMY_SP,
246                body: Some(BlockStmt {
247                    stmts: vec![apply],
248                    ..Default::default()
249                }),
250                is_generator: false,
251                is_async: false,
252
253                ..Default::default()
254            }),
255        }
256    }
257}
258
259impl From<FnExpr> for FunctionWrapper<Expr> {
260    fn from(mut fn_expr: FnExpr) -> Self {
261        let function_ident = fn_expr.ident.take();
262        let params = Self::get_params(fn_expr.function.params.iter());
263        Self {
264            binding_ident: None,
265            function_ident,
266            params,
267            ignore_function_name: false,
268            ignore_function_length: false,
269            function: fn_expr.into(),
270            _type: Default::default(),
271        }
272    }
273}
274
275impl From<ArrowExpr> for FunctionWrapper<Expr> {
276    fn from(
277        ArrowExpr {
278            span,
279            params,
280            body,
281            is_async,
282            is_generator,
283            ..
284        }: ArrowExpr,
285    ) -> Self {
286        let body = Some(match *body {
287            BlockStmtOrExpr::BlockStmt(block) => block,
288            BlockStmtOrExpr::Expr(expr) => BlockStmt {
289                stmts: vec![Stmt::Return(ReturnStmt {
290                    span: expr.span(),
291                    arg: Some(expr),
292                })],
293                ..Default::default()
294            },
295        });
296
297        let function = Box::new(Function {
298            span,
299            params: params.into_iter().map(Into::into).collect(),
300            body,
301            type_params: None,
302            return_type: None,
303            is_generator,
304            is_async,
305            ..Default::default()
306        });
307
308        let fn_expr = FnExpr {
309            ident: None,
310            function,
311        };
312
313        Self {
314            binding_ident: None,
315            function_ident: None,
316            ignore_function_name: false,
317            ignore_function_length: false,
318            params: Self::get_params(fn_expr.function.params.iter()),
319            function: fn_expr.into(),
320            _type: Default::default(),
321        }
322    }
323}
324
325#[allow(clippy::from_over_into)]
326impl Into<Expr> for FunctionWrapper<Expr> {
327    /// If a function has a function name, it may be called recursively.
328    /// We use the named expression to hoist the function name internally
329    /// Therefore, its recursive calls refer to the correct identity.
330    ///
331    /// Else
332    /// if a function has a binding name, it may be called recursively as well.
333    /// But it refer the binding name which exist the outer scope.
334    /// It is safe to using anonymous expression wrapper.
335    ///
336    /// Optimization:
337    /// A function without a name cannot be recursively referenced by Ident.
338    /// It's safe to return the expr without wrapper if the params.len is 0.
339    fn into(mut self) -> Expr {
340        if let Some(name_ident) = self.function_ident.as_ref().cloned() {
341            self.build_named_expression_wrapper(name_ident)
342        } else if (!self.ignore_function_name && self.binding_ident.is_some())
343            || (!self.ignore_function_length && !self.params.is_empty())
344        {
345            self.build_anonymous_expression_wrapper()
346        } else {
347            self.function
348        }
349    }
350}
351
352impl From<FnDecl> for FunctionWrapper<FnDecl> {
353    fn from(mut fn_decl: FnDecl) -> Self {
354        let function_ident = Some(fn_decl.ident.take());
355        let params = Self::get_params(fn_decl.function.params.iter());
356        Self {
357            binding_ident: None,
358            function_ident,
359            params,
360            ignore_function_name: false,
361            ignore_function_length: false,
362            function: FnExpr {
363                ident: None,
364                function: fn_decl.function,
365            }
366            .into(),
367            _type: Default::default(),
368        }
369    }
370}
371
372///
373/// The result of declaration wrapper includes two parts.
374/// `name_fn` is used to replace the original function.
375/// `ref_fn` is an extra function called internally by `name_fn`.
376///
377/// ```javascript
378/// function NAME(PARAMS) {
379///     return REF.apply(this, arguments);
380/// }
381/// function REF() {
382///     REF = FUNCTION;
383///     return REF.apply(this, arguments);
384/// }
385/// ```
386pub struct FnWrapperResult<N, R> {
387    pub name_fn: N,
388    pub ref_fn: R,
389}
390
391impl From<FunctionWrapper<FnDecl>> for FnWrapperResult<FnDecl, FnDecl> {
392    fn from(mut value: FunctionWrapper<FnDecl>) -> Self {
393        let name_ident = value
394            .function_ident
395            .clone()
396            .expect("`FunctionWrapper` converted from `FnDecl` definitely has `Ident`");
397
398        let (FnExpr { function, .. }, ref_fn) = value.build_declaration_wrapper(None);
399
400        FnWrapperResult {
401            name_fn: FnDecl {
402                ident: name_ident,
403                declare: false,
404                function,
405            },
406            ref_fn,
407        }
408    }
409}
410
411impl From<FunctionWrapper<Expr>> for FnWrapperResult<FnExpr, FnDecl> {
412    fn from(mut value: FunctionWrapper<Expr>) -> Self {
413        let name_ident = value
414            .function_ident
415            .clone()
416            .or_else(|| value.binding_ident.clone());
417
418        let (name_fn, ref_fn) = value.build_declaration_wrapper(name_ident);
419
420        FnWrapperResult { name_fn, ref_fn }
421    }
422}