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::Concat(_, v) => {
84 let taken = take(v);
86 let mut new: BumpVec<JsValue> = BumpVec::with_capacity_in(arena, taken.len());
87 for v in taken {
88 if v.as_str() == Some("") {
90 continue;
91 }
92 if let Some(str) = v.as_str() {
93 if let Some(last) = new.last_mut() {
94 if let Some(last_str) = last.as_str() {
95 *last = [last_str, str].concat().into();
96 } else {
97 new.push(arena, v);
98 }
99 } else {
100 new.push(arena, v);
101 }
102 } else if let JsValue::Concat(_, v) = v {
103 new.extend(arena, v);
104 } else {
105 new.push(arena, v);
106 }
107 }
108 if new.len() == 1 {
109 *self = new.into_iter().next().unwrap();
110 } else {
111 *v = new;
112 self.update_total_nodes();
113 }
114 }
115 JsValue::Add(_, v) => {
116 let taken = take(v);
117 let mut added: BumpVec<JsValue> = BumpVec::with_capacity_in(arena, taken.len());
118 let mut iter = taken.into_iter();
119 while let Some(item) = iter.next() {
120 if item.is_string() == Some(true) {
121 let mut concat: BumpVec<JsValue> = match added.len() {
122 0 => BumpVec::new(),
123 1 => BumpVec::from_iter_in(arena, [added.into_iter().next().unwrap()]),
124 _ => BumpVec::from_iter_in(
125 arena,
126 [JsValue::Add(
127 1 + added.iter().map(|v| v.total_nodes()).sum::<u32>(),
128 added,
129 )],
130 ),
131 };
132 concat.push(arena, item);
133 concat.extend(arena, iter);
134 *self = JsValue::Concat(
135 1 + concat.iter().map(|v| v.total_nodes()).sum::<u32>(),
136 concat,
137 );
138 return;
139 } else {
140 added.push(arena, item);
141 }
142 }
143 if added.len() == 1 {
144 *self = added.into_iter().next().unwrap();
145 } else {
146 *v = added;
147 self.update_total_nodes();
148 }
149 }
150 JsValue::Logical(_, op, list)
151 if list.iter().any(|v| {
154 if let JsValue::Logical(_, inner_op, _) = v {
155 inner_op == op
156 } else {
157 false
158 }
159 }) => {
160 let taken = take(list);
162 for mut v in taken {
163 if let JsValue::Logical(_, inner_op, inner_list) = &mut v {
164 if inner_op == op {
165 list.extend(arena, take(inner_list));
166 } else {
167 list.push(arena, v);
168 }
169 } else {
170 list.push(arena, v);
171 }
172 }
173 self.update_total_nodes();
174 }
175 _ => {}
176 }
177 }
178
179 pub fn normalize(&mut self, arena: &'a Bump) {
181 self.for_each_children_mut(&mut |child| {
182 child.normalize(arena);
183 true
184 });
185 self.normalize_shallow(arena);
186 }
187}
188
189