Skip to main content

turbopack_ecmascript/analyzer/jsvalue/
constants.rs

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    /// Deep-clone this object part into `arena`. See [`JsValue::clone_in`].
35    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
338/// The four categories of [JsValue]s.
339pub(super) enum JsValueMetaKind {
340    /// Doesn't contain nested values.
341    Leaf,
342    /// Contains nested values. Nested values represent some structure and can't
343    /// be replaced during linking. They might contain placeholders.
344    Nested,
345    /// Contains nested values. Operations are replaced during linking. They
346    /// might contain placeholders.
347    Operation,
348    /// These values are replaced during linking.
349    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}