turbopack_ecmascript/analyzer/jsvalue/
normalize.rs1use std::{hash::BuildHasherDefault, mem::take};
2
3use rustc_hash::FxHasher;
4use turbo_tasks::FxIndexSet;
5
6use crate::analyzer::{Bump, BumpVec, JsValue, jsvalue::similar::SimilarJsValue};
7
8impl<'a> JsValue<'a> {
10 pub(crate) fn add_alt(&mut self, arena: &'a Bump, v: Self) {
14 if self == &v {
15 return;
16 }
17
18 if let JsValue::Alternatives {
19 total_nodes: c,
20 values,
21 logical_property: _,
22 } = self
23 {
24 if !values.contains(&v) {
25 *c += v.total_nodes();
26 values.push(arena, v);
27 }
28 } else {
29 let l = take(self);
30 *self = JsValue::Alternatives {
31 total_nodes: 1 + l.total_nodes() + v.total_nodes(),
32 values: BumpVec::from_iter_in(arena, [l, v]),
33 logical_property: None,
34 };
35 }
36 }
37}
38
39impl<'a> JsValue<'a> {
41 pub fn normalize_shallow(&mut self, arena: &'a Bump) {
44 match self {
45 JsValue::Alternatives {
46 total_nodes: _,
47 values,
48 logical_property: _,
49 } => {
50 if values.len() == 1 {
51 *self = take(&mut values[0]);
52 } else {
53 let mut set = FxIndexSet::with_capacity_and_hasher(
54 values.len(),
55 BuildHasherDefault::<FxHasher>::default(),
56 );
57 let taken = take(values);
59 for v in taken {
60 match v {
61 JsValue::Alternatives {
62 total_nodes: _,
63 values,
64 logical_property: _,
65 } => {
66 for v in values {
67 set.insert(SimilarJsValue(v));
68 }
69 }
70 v => {
71 set.insert(SimilarJsValue(v));
72 }
73 }
74 }
75 if set.len() == 1 {
76 *self = set.into_iter().next().unwrap().0;
77 } else {
78 *values = BumpVec::from_iter_in(arena, set.into_iter().map(|v| v.0));
79 self.update_total_nodes();
80 }
81 }
82 }
83 JsValue::Promise(_, inner) | JsValue::Awaited(_, inner) => {
84 if resolve_promises(inner) {
85 self.update_total_nodes();
86 }
87 }
88 JsValue::Concat(_, v) => {
89 let taken = take(v);
91 let mut new: BumpVec<JsValue> = BumpVec::with_capacity_in(arena, taken.len());
92 for v in taken {
93 if v.as_str() == Some("") {
95 continue;
96 }
97 if let Some(str) = v.as_str() {
98 if let Some(last) = new.last_mut() {
99 if let Some(last_str) = last.as_str() {
100 *last = [last_str, str].concat().into();
101 } else {
102 new.push(arena, v);
103 }
104 } else {
105 new.push(arena, v);
106 }
107 } else if let JsValue::Concat(_, v) = v {
108 new.extend(arena, v);
109 } else {
110 new.push(arena, v);
111 }
112 }
113 if new.len() == 1 {
114 *self = new.into_iter().next().unwrap();
115 } else {
116 *v = new;
117 self.update_total_nodes();
118 }
119 }
120 JsValue::Add(_, v) => {
121 let taken = take(v);
122 let mut added: BumpVec<JsValue> = BumpVec::with_capacity_in(arena, taken.len());
123 let mut iter = taken.into_iter();
124 while let Some(item) = iter.next() {
125 if item.is_string() == Some(true) {
126 let mut concat: BumpVec<JsValue> = match added.len() {
127 0 => BumpVec::new(),
128 1 => BumpVec::from_iter_in(arena, [added.into_iter().next().unwrap()]),
129 _ => BumpVec::from_iter_in(
130 arena,
131 [JsValue::Add(
132 1 + added.iter().map(|v| v.total_nodes()).sum::<u32>(),
133 added,
134 )],
135 ),
136 };
137 concat.push(arena, item);
138 concat.extend(arena, iter);
139 *self = JsValue::Concat(
140 1 + concat.iter().map(|v| v.total_nodes()).sum::<u32>(),
141 concat,
142 );
143 return;
144 } else {
145 added.push(arena, item);
146 }
147 }
148 if added.len() == 1 {
149 *self = added.into_iter().next().unwrap();
150 } else {
151 *v = added;
152 self.update_total_nodes();
153 }
154 }
155 JsValue::Logical(_, op, list)
156 if list.iter().any(|v| {
159 if let JsValue::Logical(_, inner_op, _) = v {
160 inner_op == op
161 } else {
162 false
163 }
164 }) => {
165 let taken = take(list);
167 for mut v in taken {
168 if let JsValue::Logical(_, inner_op, inner_list) = &mut v {
169 if inner_op == op {
170 list.extend(arena, take(inner_list));
171 } else {
172 list.push(arena, v);
173 }
174 } else {
175 list.push(arena, v);
176 }
177 }
178 self.update_total_nodes();
179 }
180 _ => {}
181 }
182 }
183
184 pub fn normalize(&mut self, arena: &'a Bump) {
186 self.for_each_children_mut(&mut |child| {
187 child.normalize(arena);
188 true
189 });
190 self.normalize_shallow(arena);
191 }
192}
193
194pub(crate) fn resolve_promises<'a>(value: &mut JsValue<'a>) -> bool {
203 match value {
204 JsValue::Promise(_, inner) => {
205 let mut inner = take(&mut **inner);
206 resolve_promises(&mut inner);
207 *value = inner;
208 true
209 }
210 JsValue::Tenary(_, _, cons, alt) => {
213 let modified = resolve_promises(cons) | resolve_promises(alt);
214 if modified {
215 value.update_total_nodes();
216 }
217 modified
218 }
219 JsValue::Alternatives { values, .. } => {
220 let mut modified = false;
221 for alternative in values.iter_mut() {
222 modified |= resolve_promises(alternative);
223 }
224 if modified {
225 let values = take(values);
226 *value = JsValue::alternatives(values);
227 }
228 modified
229 }
230 _ => false,
231 }
232}
233
234