Skip to main content

turbopack_ecmascript/analyzer/jsvalue/
traverse.rs

1use crate::analyzer::{JsValue, ObjectPart};
2
3// Visiting
4impl<'a> JsValue<'a> {
5    /// Calls a function for each child of the node. Allows mutating the node.
6    /// Updates the total nodes count after mutation.
7    pub fn for_each_children_mut(
8        &mut self,
9        visitor: &mut impl FnMut(&mut JsValue<'a>) -> bool,
10    ) -> bool {
11        match self {
12            JsValue::Alternatives {
13                total_nodes: _,
14                values: list,
15                logical_property: _,
16            }
17            | JsValue::Concat(_, list)
18            | JsValue::Add(_, list)
19            | JsValue::Logical(_, _, list)
20            | JsValue::Array { items: list, .. } => {
21                let mut modified = false;
22                for item in list.iter_mut() {
23                    if visitor(item) {
24                        modified = true
25                    }
26                }
27                if modified {
28                    self.update_total_nodes();
29                }
30                modified
31            }
32            JsValue::Not(_, value) => {
33                let modified = visitor(value);
34                if modified {
35                    self.update_total_nodes();
36                }
37                modified
38            }
39            JsValue::Object { parts, .. } => {
40                let mut modified = false;
41                for item in parts.iter_mut() {
42                    match item {
43                        ObjectPart::KeyValue(key, value) => {
44                            if visitor(key) {
45                                modified = true
46                            }
47                            if visitor(value) {
48                                modified = true
49                            }
50                        }
51                        ObjectPart::Spread(value) => {
52                            if visitor(value) {
53                                modified = true
54                            }
55                        }
56                    }
57                }
58                if modified {
59                    self.update_total_nodes();
60                }
61                modified
62            }
63            JsValue::New(_, call) => {
64                let modified = call.for_each_children_mut(visitor);
65                if modified {
66                    self.update_total_nodes();
67                }
68                modified
69            }
70            JsValue::Call(_, call) => {
71                let modified = call.for_each_children_mut(visitor);
72                if modified {
73                    self.update_total_nodes();
74                }
75                modified
76            }
77            JsValue::SuperCall(_, args) => {
78                let mut modified = false;
79                for item in args.iter_mut() {
80                    if visitor(item) {
81                        modified = true
82                    }
83                }
84                if modified {
85                    self.update_total_nodes();
86                }
87                modified
88            }
89            JsValue::MemberCall(_, call) => {
90                let modified = call.for_each_children_mut(visitor);
91
92                if modified {
93                    self.update_total_nodes();
94                }
95                modified
96            }
97            JsValue::Function(_, _, return_value) => {
98                let modified = visitor(return_value);
99
100                if modified {
101                    self.update_total_nodes();
102                }
103                modified
104            }
105            JsValue::Binary(_, a, _, b) => {
106                let m1 = visitor(a);
107                let m2 = visitor(b);
108                let modified = m1 || m2;
109                if modified {
110                    self.update_total_nodes();
111                }
112                modified
113            }
114            JsValue::Tenary(_, test, cons, alt) => {
115                let m1 = visitor(test);
116                let m2 = visitor(cons);
117                let m3 = visitor(alt);
118                let modified = m1 || m2 || m3;
119                if modified {
120                    self.update_total_nodes();
121                }
122                modified
123            }
124            JsValue::Member(_, obj, prop) | JsValue::In(_, obj, prop) => {
125                let m1 = visitor(obj);
126                let m2 = visitor(prop);
127                let modified = m1 || m2;
128                if modified {
129                    self.update_total_nodes();
130                }
131                modified
132            }
133            JsValue::Iterated(_, operand)
134            | JsValue::TypeOf(_, operand)
135            | JsValue::Promise(_, operand)
136            | JsValue::Awaited(_, operand) => {
137                let modified = visitor(operand);
138                if modified {
139                    self.update_total_nodes();
140                }
141                modified
142            }
143
144            JsValue::Constant(_)
145            | JsValue::FreeVar(_)
146            | JsValue::Variable(_)
147            | JsValue::Module(..)
148            | JsValue::Url(_, _)
149            | JsValue::WellKnownObject(_)
150            | JsValue::WellKnownFunction(_)
151            | JsValue::Unknown { .. }
152            | JsValue::Argument(..) => false,
153        }
154    }
155
156    /// Calls a function for only early children. Allows mutating the
157    /// node. Updates the total nodes count after mutation.
158    pub fn for_each_early_children_mut(
159        &mut self,
160        visitor: &mut impl FnMut(&mut JsValue<'a>) -> bool,
161    ) -> bool {
162        match self {
163            JsValue::New(_, call) if !call.args().is_empty() => {
164                let m = visitor(call.callee_mut());
165                if m {
166                    self.update_total_nodes();
167                }
168                m
169            }
170            JsValue::Call(_, call) if !call.args().is_empty() => {
171                let m = visitor(call.callee_mut());
172                if m {
173                    self.update_total_nodes();
174                }
175                m
176            }
177            JsValue::MemberCall(_, call) if !call.args().is_empty() => {
178                let m1 = visitor(call.prop_mut());
179                let m2 = visitor(call.obj_mut());
180                let modified = m1 || m2;
181                if modified {
182                    self.update_total_nodes();
183                }
184                modified
185            }
186            JsValue::Member(_, obj, _) => {
187                let m = visitor(obj);
188                if m {
189                    self.update_total_nodes();
190                }
191                m
192            }
193            _ => false,
194        }
195    }
196
197    /// Calls a function for only late children. Allows mutating the
198    /// node. Updates the total nodes count after mutation.
199    pub fn for_each_late_children_mut(
200        &mut self,
201        visitor: &mut impl FnMut(&mut JsValue<'a>) -> bool,
202    ) -> bool {
203        match self {
204            JsValue::New(_, call) if !call.args().is_empty() => {
205                let mut modified = false;
206                for item in call.args_mut().iter_mut() {
207                    if visitor(item) {
208                        modified = true
209                    }
210                }
211                if modified {
212                    self.update_total_nodes();
213                }
214                modified
215            }
216            JsValue::Call(_, call) if !call.args().is_empty() => {
217                let mut modified = false;
218                for item in call.args_mut().iter_mut() {
219                    if visitor(item) {
220                        modified = true
221                    }
222                }
223                if modified {
224                    self.update_total_nodes();
225                }
226                modified
227            }
228            JsValue::MemberCall(_, call) if !call.args().is_empty() => {
229                let mut modified = false;
230                for item in call.args_mut().iter_mut() {
231                    if visitor(item) {
232                        modified = true
233                    }
234                }
235                if modified {
236                    self.update_total_nodes();
237                }
238                modified
239            }
240            JsValue::Member(_, _, prop) => {
241                let m = visitor(prop);
242                if m {
243                    self.update_total_nodes();
244                }
245                m
246            }
247            _ => self.for_each_children_mut(visitor),
248        }
249    }
250
251    /// Visit the node and all its children with a function.
252    pub fn visit(&self, visitor: &mut impl FnMut(&JsValue<'a>)) {
253        self.for_each_children(&mut |value| value.visit(visitor));
254        visitor(self);
255    }
256
257    /// Calls a function for all children of the node.
258    pub fn for_each_children(&self, visitor: &mut impl FnMut(&JsValue<'a>)) {
259        match self {
260            JsValue::Alternatives {
261                total_nodes: _,
262                values: list,
263                logical_property: _,
264            }
265            | JsValue::Concat(_, list)
266            | JsValue::Add(_, list)
267            | JsValue::Logical(_, _, list)
268            | JsValue::Array { items: list, .. } => {
269                for item in list.iter() {
270                    visitor(item);
271                }
272            }
273            JsValue::Not(_, value) => {
274                visitor(value);
275            }
276            JsValue::Object { parts, .. } => {
277                for item in parts.iter() {
278                    match item {
279                        ObjectPart::KeyValue(key, value) => {
280                            visitor(key);
281                            visitor(value);
282                        }
283                        ObjectPart::Spread(value) => {
284                            visitor(value);
285                        }
286                    }
287                }
288            }
289            JsValue::New(_, call) => {
290                call.for_each_children(visitor);
291            }
292            JsValue::Call(_, call) => {
293                call.for_each_children(visitor);
294            }
295            JsValue::SuperCall(_, args) => {
296                for item in args.iter() {
297                    visitor(item);
298                }
299            }
300            JsValue::MemberCall(_, call) => {
301                call.for_each_children(visitor);
302            }
303            JsValue::Function(_, _, return_value) => {
304                visitor(return_value);
305            }
306            JsValue::Member(_, obj, prop) | JsValue::In(_, obj, prop) => {
307                visitor(obj);
308                visitor(prop);
309            }
310            JsValue::Binary(_, a, _, b) => {
311                visitor(a);
312                visitor(b);
313            }
314            JsValue::Tenary(_, test, cons, alt) => {
315                visitor(test);
316                visitor(cons);
317                visitor(alt);
318            }
319
320            JsValue::Iterated(_, operand)
321            | JsValue::TypeOf(_, operand)
322            | JsValue::Promise(_, operand)
323            | JsValue::Awaited(_, operand) => {
324                visitor(operand);
325            }
326
327            JsValue::Constant(_)
328            | JsValue::FreeVar(_)
329            | JsValue::Variable(_)
330            | JsValue::Module(..)
331            | JsValue::Url(_, _)
332            | JsValue::WellKnownObject(_)
333            | JsValue::WellKnownFunction(_)
334            | JsValue::Unknown { .. }
335            | JsValue::Argument(..) => {}
336        }
337    }
338}