1use std::{
2 borrow::Cow,
3 fmt::{Display, Formatter},
4 hash::{Hash, Hasher},
5 sync::Arc,
6};
7
8use num_bigint::BigInt;
9use num_traits::Zero;
10use swc_core::{
11 atoms::Wtf8Atom,
12 ecma::{ast::Lit, atoms::Atom},
13};
14use turbo_rcstr::RcStr;
15
16use crate::{
17 analyzer::{Bump, JsValue, imports::ImportAnnotations},
18 utils::StringifyJs,
19};
20
21#[derive(Debug, Hash, PartialEq)]
22pub enum ObjectPart<'a> {
23 KeyValue(JsValue<'a>, JsValue<'a>),
24 Spread(JsValue<'a>),
25}
26
27impl Default for ObjectPart<'_> {
28 fn default() -> Self {
29 ObjectPart::Spread(Default::default())
30 }
31}
32
33impl<'a> ObjectPart<'a> {
34 pub(crate) fn clone_in(&self, arena: &'a Bump) -> Self {
36 match self {
37 ObjectPart::KeyValue(k, v) => {
38 ObjectPart::KeyValue(k.clone_in(arena), v.clone_in(arena))
39 }
40 ObjectPart::Spread(s) => ObjectPart::Spread(s.clone_in(arena)),
41 }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct ConstantNumber(pub f64);
47
48impl ConstantNumber {
49 pub fn as_u32_index(&self) -> Option<usize> {
50 let index: u32 = self.0 as u32;
51 (index as f64 == self.0).then_some(index as usize)
52 }
53}
54
55impl Hash for ConstantNumber {
56 fn hash<H: Hasher>(&self, state: &mut H) {
57 self.0.to_ne_bytes().hash(state);
58 }
59}
60
61impl From<f64> for ConstantNumber {
62 fn from(value: f64) -> Self {
63 ConstantNumber(value)
64 }
65}
66
67#[derive(Debug, Clone)]
68pub enum ConstantString {
69 Atom(Atom),
70 RcStr(RcStr),
71}
72
73impl ConstantString {
74 pub fn as_str(&self) -> &str {
75 match self {
76 Self::Atom(s) => s,
77 Self::RcStr(s) => s,
78 }
79 }
80
81 pub fn as_rcstr(&self) -> RcStr {
82 match self {
83 Self::Atom(s) => RcStr::from(s.as_str()),
84 Self::RcStr(s) => s.clone(),
85 }
86 }
87
88 pub fn as_atom(&self) -> Cow<'_, Atom> {
89 match self {
90 Self::Atom(s) => Cow::Borrowed(s),
91 Self::RcStr(s) => Cow::Owned(s.as_str().into()),
92 }
93 }
94
95 pub fn is_empty(&self) -> bool {
96 self.as_str().is_empty()
97 }
98}
99
100impl PartialEq for ConstantString {
101 fn eq(&self, other: &Self) -> bool {
102 self.as_str() == other.as_str()
103 }
104}
105
106impl Eq for ConstantString {}
107
108impl Hash for ConstantString {
109 fn hash<H: Hasher>(&self, state: &mut H) {
110 self.as_str().hash(state);
111 }
112}
113
114impl Display for ConstantString {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 self.as_str().fmt(f)
117 }
118}
119
120impl From<Atom> for ConstantString {
121 fn from(v: Atom) -> Self {
122 ConstantString::Atom(v)
123 }
124}
125
126impl From<&'static str> for ConstantString {
127 fn from(v: &'static str) -> Self {
128 ConstantString::Atom(v.into())
129 }
130}
131
132impl From<String> for ConstantString {
133 fn from(v: String) -> Self {
134 ConstantString::Atom(v.into())
135 }
136}
137
138impl From<RcStr> for ConstantString {
139 fn from(v: RcStr) -> Self {
140 ConstantString::RcStr(v)
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Default, Hash)]
145pub enum ConstantValue {
146 #[default]
147 Undefined,
148 Str(ConstantString),
149 Num(ConstantNumber),
150 True,
151 False,
152 Null,
153 BigInt(Box<BigInt>),
154 Regex(Box<(Atom, Atom)>),
155}
156
157impl ConstantValue {
158 pub fn as_str(&self) -> Option<&str> {
159 match self {
160 Self::Str(s) => Some(s.as_str()),
161 _ => None,
162 }
163 }
164
165 pub fn as_bool(&self) -> Option<bool> {
166 match self {
167 Self::True => Some(true),
168 Self::False => Some(false),
169 _ => None,
170 }
171 }
172
173 pub fn is_truthy(&self) -> bool {
174 match self {
175 Self::Undefined | Self::False | Self::Null => false,
176 Self::True | Self::Regex(..) => true,
177 Self::Str(s) => !s.is_empty(),
178 Self::Num(ConstantNumber(n)) => *n != 0.0,
179 Self::BigInt(n) => !n.is_zero(),
180 }
181 }
182
183 pub fn is_nullish(&self) -> bool {
184 match self {
185 Self::Undefined | Self::Null => true,
186 Self::Str(..)
187 | Self::Num(..)
188 | Self::True
189 | Self::False
190 | Self::BigInt(..)
191 | Self::Regex(..) => false,
192 }
193 }
194
195 pub fn is_empty_string(&self) -> bool {
196 match self {
197 Self::Str(s) => s.is_empty(),
198 _ => false,
199 }
200 }
201
202 pub fn is_value_type(&self) -> bool {
203 !matches!(self, Self::Regex(..))
204 }
205}
206
207impl From<bool> for ConstantValue {
208 fn from(v: bool) -> Self {
209 match v {
210 true => ConstantValue::True,
211 false => ConstantValue::False,
212 }
213 }
214}
215
216impl From<&'_ str> for ConstantValue {
217 fn from(v: &str) -> Self {
218 ConstantValue::Str(ConstantString::Atom(v.into()))
219 }
220}
221
222impl From<Lit> for ConstantValue {
223 fn from(v: Lit) -> Self {
224 match v {
225 Lit::Str(v) => {
226 ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned()))
227 }
228 Lit::Bool(v) => {
229 if v.value {
230 ConstantValue::True
231 } else {
232 ConstantValue::False
233 }
234 }
235 Lit::Null(_) => ConstantValue::Null,
236 Lit::Num(v) => ConstantValue::Num(ConstantNumber(v.value)),
237 Lit::BigInt(v) => ConstantValue::BigInt(v.value),
238 Lit::Regex(v) => ConstantValue::Regex(Box::new((v.exp, v.flags))),
239 Lit::JSXText(v) => ConstantValue::Str(ConstantString::Atom(v.value)),
240 }
241 }
242}
243
244impl Display for ConstantValue {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 match self {
247 ConstantValue::Undefined => write!(f, "undefined"),
248 ConstantValue::Str(str) => write!(f, "{}", StringifyJs(str.as_str())),
249 ConstantValue::True => write!(f, "true"),
250 ConstantValue::False => write!(f, "false"),
251 ConstantValue::Null => write!(f, "null"),
252 ConstantValue::Num(ConstantNumber(n)) => write!(f, "{n}"),
253 ConstantValue::BigInt(n) => write!(f, "{n}"),
254 ConstantValue::Regex(regex) => write!(f, "/{}/{}", regex.0, regex.1),
255 }
256 }
257}
258
259#[derive(Debug, Clone, Hash, PartialEq, Eq)]
260pub struct ModuleValue {
261 pub module: Wtf8Atom,
262 pub annotations: Option<Arc<ImportAnnotations>>,
263}
264
265#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
266pub enum LogicalOperator {
267 And,
268 Or,
269 NullishCoalescing,
270}
271
272impl LogicalOperator {
273 pub(super) fn joiner(&self) -> &'static str {
274 match self {
275 LogicalOperator::And => " && ",
276 LogicalOperator::Or => " || ",
277 LogicalOperator::NullishCoalescing => " ?? ",
278 }
279 }
280 pub(super) fn multi_line_joiner(&self) -> &'static str {
281 match self {
282 LogicalOperator::And => "&& ",
283 LogicalOperator::Or => "|| ",
284 LogicalOperator::NullishCoalescing => "?? ",
285 }
286 }
287}
288
289#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
290pub enum BinaryOperator {
291 Equal,
292 NotEqual,
293 StrictEqual,
294 StrictNotEqual,
295}
296
297impl BinaryOperator {
298 pub(super) fn joiner(&self) -> &'static str {
299 match self {
300 BinaryOperator::Equal => " == ",
301 BinaryOperator::NotEqual => " != ",
302 BinaryOperator::StrictEqual => " === ",
303 BinaryOperator::StrictNotEqual => " !== ",
304 }
305 }
306
307 pub(super) fn positive_op(&self) -> (PositiveBinaryOperator, bool) {
308 match self {
309 BinaryOperator::Equal => (PositiveBinaryOperator::Equal, false),
310 BinaryOperator::NotEqual => (PositiveBinaryOperator::Equal, true),
311 BinaryOperator::StrictEqual => (PositiveBinaryOperator::StrictEqual, false),
312 BinaryOperator::StrictNotEqual => (PositiveBinaryOperator::StrictEqual, true),
313 }
314 }
315}
316
317#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
318pub enum PositiveBinaryOperator {
319 Equal,
320 StrictEqual,
321}
322
323#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
324pub enum JsValueUrlKind {
325 Absolute,
326 Relative,
327}
328
329impl Display for JsValueUrlKind {
330 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
331 f.write_str(match self {
332 JsValueUrlKind::Absolute => "absolute",
333 JsValueUrlKind::Relative => "relative",
334 })
335 }
336}
337
338pub(super) enum JsValueMetaKind {
340 Leaf,
342 Nested,
345 Operation,
348 Placeholder,
350}
351
352#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
353pub enum LogicalProperty {
354 Truthy,
355 Falsy,
356 Nullish,
357 NonNullish,
358}
359
360impl Display for LogicalProperty {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 match self {
363 LogicalProperty::Truthy => write!(f, "truthy"),
364 LogicalProperty::Falsy => write!(f, "falsy"),
365 LogicalProperty::Nullish => write!(f, "nullish"),
366 LogicalProperty::NonNullish => write!(f, "non-nullish"),
367 }
368 }
369}