swc_ecma_compat_es2022/class_properties/
member_init.rs

1use swc_common::{Span, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_transforms_base::helper;
4use swc_ecma_utils::{prop_name_to_expr, prop_name_to_expr_value, quote_ident, ExprFactory};
5use swc_trace_macro::swc_trace;
6
7use super::Config;
8
9pub(super) enum MemberInit {
10    PubProp(PubProp),
11    PrivProp(PrivProp),
12    PrivMethod(PrivMethod),
13    PrivAccessor(PrivAccessor),
14    StaticBlock(Box<Expr>),
15}
16
17pub(super) struct PubProp {
18    pub span: Span,
19    pub name: PropName,
20    pub value: Box<Expr>,
21}
22
23pub(super) struct PrivProp {
24    pub span: Span,
25    pub name: Ident,
26    pub value: Box<Expr>,
27}
28
29pub(super) struct PrivMethod {
30    pub span: Span,
31    pub name: Ident,
32    // only used in loose mode
33    pub fn_name: Ident,
34}
35
36pub(super) struct PrivAccessor {
37    pub span: Span,
38    pub name: Ident,
39    pub getter: Option<Ident>,
40    pub setter: Option<Ident>,
41}
42
43pub(super) struct MemberInitRecord {
44    c: Config,
45    pub record: Vec<MemberInit>,
46}
47
48#[swc_trace]
49impl MemberInitRecord {
50    pub fn new(c: Config) -> Self {
51        Self {
52            c,
53            record: Vec::new(),
54        }
55    }
56
57    pub fn push(&mut self, member: MemberInit) -> bool {
58        // there shouldn't be many class field, so n^2 should be fine
59        if let MemberInit::PrivAccessor(accessor) = member {
60            if let Some(MemberInit::PrivAccessor(previous)) =
61                self.record.iter_mut().find(|item| matches!(item, MemberInit::PrivAccessor(PrivAccessor { name, .. }) if name.sym == accessor.name.sym))
62            {
63                previous.getter = previous.getter.take().or(accessor.getter);
64                previous.setter = previous.setter.take().or(accessor.setter);
65                false
66            } else {
67                self.record.push(MemberInit::PrivAccessor(accessor));
68                true
69            }
70        } else {
71            self.record.push(member);
72            true
73        }
74    }
75
76    pub fn into_init(self) -> Vec<Box<Expr>> {
77        let mut normal_init = Vec::new();
78        let mut value_init = Vec::new();
79        for init in self.record {
80            match init {
81                MemberInit::PrivMethod(PrivMethod {
82                    span,
83                    name,
84                    fn_name,
85                }) => {
86                    let (callee, args) = if self.c.private_as_properties {
87                        (
88                            obj_def_prop(),
89                            vec![
90                                ThisExpr { span: DUMMY_SP }.as_arg(),
91                                name.as_arg(),
92                                get_method_desc(Box::new(fn_name.into())).as_arg(),
93                            ],
94                        )
95                    } else {
96                        (
97                            helper!(class_private_method_init),
98                            vec![ThisExpr { span: DUMMY_SP }.as_arg(), name.as_arg()],
99                        )
100                    };
101                    normal_init.push(
102                        CallExpr {
103                            span,
104                            callee,
105                            args,
106                            ..Default::default()
107                        }
108                        .into(),
109                    )
110                }
111                MemberInit::PrivProp(PrivProp { span, name, value }) => value_init.push(
112                    CallExpr {
113                        span,
114                        callee: if self.c.private_as_properties {
115                            obj_def_prop()
116                        } else {
117                            helper!(class_private_field_init)
118                        },
119                        args: vec![
120                            ThisExpr { span: DUMMY_SP }.as_arg(),
121                            name.as_arg(),
122                            get_value_desc(value).as_arg(),
123                        ],
124                        ..Default::default()
125                    }
126                    .into(),
127                ),
128                MemberInit::PrivAccessor(PrivAccessor {
129                    span,
130                    name,
131                    getter,
132                    setter,
133                }) => normal_init.push(
134                    CallExpr {
135                        span,
136                        callee: if self.c.private_as_properties {
137                            obj_def_prop()
138                        } else {
139                            helper!(class_private_field_init)
140                        },
141                        args: vec![
142                            ThisExpr { span: DUMMY_SP }.as_arg(),
143                            name.as_arg(),
144                            get_accessor_desc(getter, setter).as_arg(),
145                        ],
146                        ..Default::default()
147                    }
148                    .into(),
149                ),
150                MemberInit::PubProp(PubProp { span, name, value }) => value_init.push(
151                    if self.c.set_public_fields {
152                        let this = ThisExpr { span: DUMMY_SP };
153                        Expr::from(AssignExpr {
154                            span,
155                            left: match name {
156                                PropName::Ident(id) => this.make_member(id).into(),
157                                _ => this.computed_member(prop_name_to_expr(name)).into(),
158                            },
159                            op: op!("="),
160                            right: value,
161                        })
162                    } else {
163                        CallExpr {
164                            span,
165                            callee: helper!(define_property),
166                            args: vec![
167                                ThisExpr { span: DUMMY_SP }.as_arg(),
168                                prop_name_to_expr_value(name).as_arg(),
169                                value.as_arg(),
170                            ],
171                            ..Default::default()
172                        }
173                        .into()
174                    }
175                    .into(),
176                ),
177                MemberInit::StaticBlock(..) => unreachable!(),
178            }
179        }
180
181        normal_init.extend(value_init);
182
183        normal_init
184    }
185
186    pub fn into_init_static(self, class_ident: Ident) -> Vec<Stmt> {
187        let mut normal_init = Vec::new();
188        let mut value_init = Vec::new();
189
190        for value in self.record {
191            match value {
192                MemberInit::PubProp(PubProp { span, name, value }) => value_init.push(
193                    ExprStmt {
194                        span,
195                        expr: (if self.c.set_public_fields {
196                            let class = class_ident.clone();
197                            Expr::from(AssignExpr {
198                                span,
199                                left: match name {
200                                    PropName::Ident(id) => class.make_member(id).into(),
201                                    _ => class.computed_member(prop_name_to_expr(name)).into(),
202                                },
203                                op: op!("="),
204                                right: value,
205                            })
206                        } else {
207                            CallExpr {
208                                span,
209                                callee: helper!(define_property),
210                                args: vec![
211                                    class_ident.clone().as_arg(),
212                                    prop_name_to_expr_value(name).as_arg(),
213                                    value.as_arg(),
214                                ],
215                                ..Default::default()
216                            }
217                            .into()
218                        })
219                        .into(),
220                    }
221                    .into(),
222                ),
223                MemberInit::PrivProp(PrivProp { span, name, value }) => {
224                    value_init.push(if self.c.private_as_properties {
225                        ExprStmt {
226                            span,
227                            expr: CallExpr {
228                                span,
229                                callee: obj_def_prop(),
230                                args: vec![
231                                    class_ident.clone().as_arg(),
232                                    name.as_arg(),
233                                    get_value_desc(value).as_arg(),
234                                ],
235                                ..Default::default()
236                            }
237                            .into(),
238                        }
239                        .into()
240                    } else {
241                        VarDecl {
242                            span,
243                            kind: VarDeclKind::Var,
244                            decls: vec![VarDeclarator {
245                                span,
246                                name: name.into(),
247                                init: Some(Expr::Object(get_value_desc(value)).into()),
248                                definite: false,
249                            }],
250                            ..Default::default()
251                        }
252                        .into()
253                    })
254                }
255                MemberInit::PrivAccessor(PrivAccessor {
256                    span,
257                    name,
258                    getter,
259                    setter,
260                }) => normal_init.push(if self.c.private_as_properties {
261                    ExprStmt {
262                        span,
263                        expr: CallExpr {
264                            span,
265                            callee: obj_def_prop(),
266                            args: vec![
267                                class_ident.clone().as_arg(),
268                                name.as_arg(),
269                                get_accessor_desc(getter, setter).as_arg(),
270                            ],
271                            ..Default::default()
272                        }
273                        .into(),
274                    }
275                    .into()
276                } else {
277                    VarDecl {
278                        span,
279                        kind: VarDeclKind::Var,
280                        decls: vec![VarDeclarator {
281                            span,
282                            name: name.into(),
283                            init: Some(Expr::Object(get_accessor_desc(getter, setter)).into()),
284                            definite: false,
285                        }],
286                        ..Default::default()
287                    }
288                    .into()
289                }),
290                MemberInit::PrivMethod(PrivMethod {
291                    span,
292                    name,
293                    fn_name,
294                }) => {
295                    if self.c.private_as_properties {
296                        normal_init.push(
297                            ExprStmt {
298                                span,
299                                expr: CallExpr {
300                                    span,
301                                    callee: obj_def_prop(),
302                                    args: vec![
303                                        class_ident.clone().as_arg(),
304                                        name.as_arg(),
305                                        get_method_desc(Box::new(fn_name.into())).as_arg(),
306                                    ],
307                                    ..Default::default()
308                                }
309                                .into(),
310                            }
311                            .into(),
312                        )
313                    } else {
314                        unreachable!()
315                    }
316                }
317                MemberInit::StaticBlock(expr) => value_init.push(expr.into_stmt()),
318            }
319        }
320
321        normal_init.extend(value_init);
322
323        normal_init
324    }
325}
326
327fn get_value_desc(value: Box<Expr>) -> ObjectLit {
328    ObjectLit {
329        span: DUMMY_SP,
330        props: vec![
331            // writeable: true
332            PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
333                key: PropName::Ident(quote_ident!("writable")),
334                value: true.into(),
335            }))),
336            // value: value,
337            PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
338                key: PropName::Ident(quote_ident!("value")),
339                value,
340            }))),
341        ],
342    }
343}
344
345fn get_accessor_desc(getter: Option<Ident>, setter: Option<Ident>) -> ObjectLit {
346    ObjectLit {
347        span: DUMMY_SP,
348        props: vec![
349            PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
350                key: PropName::Ident(quote_ident!("get")),
351                value: getter
352                    .map(|id| Box::new(id.into()))
353                    .unwrap_or_else(|| Expr::undefined(DUMMY_SP)),
354            }))),
355            PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
356                key: PropName::Ident(quote_ident!("set")),
357                value: setter
358                    .map(|id| Box::new(id.into()))
359                    .unwrap_or_else(|| Expr::undefined(DUMMY_SP)),
360            }))),
361        ],
362    }
363}
364
365fn get_method_desc(value: Box<Expr>) -> ObjectLit {
366    ObjectLit {
367        span: DUMMY_SP,
368        props: vec![
369            // value: value,
370            PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
371                key: PropName::Ident(quote_ident!("value")),
372                value,
373            }))),
374        ],
375    }
376}
377
378fn obj_def_prop() -> Callee {
379    quote_ident!("Object")
380        .make_member(quote_ident!("defineProperty"))
381        .as_callee()
382}