1use std::{
2 borrow::Cow,
3 fmt::{Display, Formatter},
4 hash::{Hash, Hasher},
5 num::NonZeroU32,
6 sync::Arc,
7};
8
9use bincode::{
10 Decode, Encode,
11 de::Decoder,
12 enc::Encoder,
13 error::{DecodeError, EncodeError},
14 impl_borrow_decode,
15};
16use num_bigint::BigInt;
17use num_traits::Zero;
18use swc_core::{
19 atoms::Wtf8Atom,
20 ecma::{ast::Lit, atoms::Atom},
21};
22use turbo_rcstr::RcStr;
23use turbo_tasks::NonLocalValue;
24
25use crate::{
26 analyzer::{Bump, JsValue, imports::ImportAnnotations},
27 utils::StringifyJs,
28};
29
30#[derive(Debug, Hash, PartialEq)]
31pub enum ObjectPart<'a> {
32 KeyValue(JsValue<'a>, JsValue<'a>),
33 Spread(JsValue<'a>),
34}
35
36impl Default for ObjectPart<'_> {
37 fn default() -> Self {
38 ObjectPart::Spread(Default::default())
39 }
40}
41
42impl<'a> ObjectPart<'a> {
43 pub(crate) fn clone_in(&self, arena: &'a Bump) -> Self {
45 match self {
46 ObjectPart::KeyValue(k, v) => {
47 ObjectPart::KeyValue(k.clone_in(arena), v.clone_in(arena))
48 }
49 ObjectPart::Spread(s) => ObjectPart::Spread(s.clone_in(arena)),
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Encode, Decode)]
55pub struct ConstantNumber(pub f64);
56
57impl ConstantNumber {
58 pub fn as_u32_index(&self) -> Option<usize> {
59 let index: u32 = self.0 as u32;
60 (index as f64 == self.0).then_some(index as usize)
61 }
62}
63
64impl Hash for ConstantNumber {
65 fn hash<H: Hasher>(&self, state: &mut H) {
66 self.0.to_ne_bytes().hash(state);
67 }
68}
69
70impl From<f64> for ConstantNumber {
71 fn from(value: f64) -> Self {
72 ConstantNumber(value)
73 }
74}
75
76#[derive(Debug, Clone)]
77pub enum ConstantString {
78 Atom(Atom),
79 RcStr(RcStr),
80}
81unsafe impl NonLocalValue for ConstantString {}
83impl Encode for ConstantString {
84 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
85 match self {
86 ConstantString::Atom(s) => s.as_str().encode(encoder),
87 ConstantString::RcStr(s) => s.as_str().encode(encoder),
88 }
89 }
90}
91impl<Context> Decode<Context> for ConstantString {
92 fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
93 Ok(Self::RcStr(Decode::decode(decoder)?))
94 }
95}
96impl_borrow_decode!(ConstantString);
97
98impl ConstantString {
99 pub fn as_str(&self) -> &str {
100 match self {
101 Self::Atom(s) => s,
102 Self::RcStr(s) => s,
103 }
104 }
105
106 pub fn as_rcstr(&self) -> RcStr {
107 match self {
108 Self::Atom(s) => RcStr::from(s.as_str()),
109 Self::RcStr(s) => s.clone(),
110 }
111 }
112
113 pub fn as_atom(&self) -> Cow<'_, Atom> {
114 match self {
115 Self::Atom(s) => Cow::Borrowed(s),
116 Self::RcStr(s) => Cow::Owned(s.as_str().into()),
117 }
118 }
119
120 pub fn is_empty(&self) -> bool {
121 self.as_str().is_empty()
122 }
123}
124
125impl PartialEq for ConstantString {
126 fn eq(&self, other: &Self) -> bool {
127 self.as_str() == other.as_str()
128 }
129}
130
131impl Eq for ConstantString {}
132
133impl Hash for ConstantString {
134 fn hash<H: Hasher>(&self, state: &mut H) {
135 self.as_str().hash(state);
136 }
137}
138
139impl Display for ConstantString {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 self.as_str().fmt(f)
142 }
143}
144
145impl From<Atom> for ConstantString {
146 fn from(v: Atom) -> Self {
147 ConstantString::Atom(v)
148 }
149}
150
151impl From<&'static str> for ConstantString {
152 fn from(v: &'static str) -> Self {
153 ConstantString::Atom(v.into())
154 }
155}
156
157impl From<String> for ConstantString {
158 fn from(v: String) -> Self {
159 ConstantString::Atom(v.into())
160 }
161}
162
163impl From<RcStr> for ConstantString {
164 fn from(v: RcStr) -> Self {
165 ConstantString::RcStr(v)
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Default, Hash, Encode, Decode)]
170pub enum ConstantValue {
171 #[default]
172 Undefined,
173 Str(ConstantString),
174 Num(ConstantNumber),
175 True,
176 False,
177 Null,
178 BigInt(#[bincode(with_serde)] Box<BigInt>),
179 Regex(#[bincode(with_serde)] Box<(Atom, Atom)>),
180}
181unsafe impl NonLocalValue for ConstantValue {}
182
183impl ConstantValue {
184 pub fn as_str(&self) -> Option<&str> {
185 match self {
186 Self::Str(s) => Some(s.as_str()),
187 _ => None,
188 }
189 }
190
191 pub fn as_bool(&self) -> Option<bool> {
192 match self {
193 Self::True => Some(true),
194 Self::False => Some(false),
195 _ => None,
196 }
197 }
198
199 pub fn is_truthy(&self) -> bool {
200 match self {
201 Self::Undefined | Self::False | Self::Null => false,
202 Self::True | Self::Regex(..) => true,
203 Self::Str(s) => !s.is_empty(),
204 Self::Num(ConstantNumber(n)) => *n != 0.0,
205 Self::BigInt(n) => !n.is_zero(),
206 }
207 }
208
209 pub fn is_nullish(&self) -> bool {
210 match self {
211 Self::Undefined | Self::Null => true,
212 Self::Str(..)
213 | Self::Num(..)
214 | Self::True
215 | Self::False
216 | Self::BigInt(..)
217 | Self::Regex(..) => false,
218 }
219 }
220
221 pub fn is_empty_string(&self) -> bool {
222 match self {
223 Self::Str(s) => s.is_empty(),
224 _ => false,
225 }
226 }
227
228 pub fn is_value_type(&self) -> bool {
229 !matches!(self, Self::Regex(..))
230 }
231}
232
233impl From<bool> for ConstantValue {
234 fn from(v: bool) -> Self {
235 match v {
236 true => ConstantValue::True,
237 false => ConstantValue::False,
238 }
239 }
240}
241
242impl From<&'_ str> for ConstantValue {
243 fn from(v: &str) -> Self {
244 ConstantValue::Str(ConstantString::Atom(v.into()))
245 }
246}
247
248impl From<Lit> for ConstantValue {
249 fn from(v: Lit) -> Self {
250 match v {
251 Lit::Str(v) => {
252 ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned()))
253 }
254 Lit::Bool(v) => {
255 if v.value {
256 ConstantValue::True
257 } else {
258 ConstantValue::False
259 }
260 }
261 Lit::Null(_) => ConstantValue::Null,
262 Lit::Num(v) => ConstantValue::Num(ConstantNumber(v.value)),
263 Lit::BigInt(v) => ConstantValue::BigInt(v.value),
264 Lit::Regex(v) => ConstantValue::Regex(Box::new((v.exp, v.flags))),
265 Lit::JSXText(v) => {
266 ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned()))
268 }
269 }
270 }
271}
272
273impl Display for ConstantValue {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 match self {
276 ConstantValue::Undefined => write!(f, "undefined"),
277 ConstantValue::Str(str) => write!(f, "{}", StringifyJs(str.as_str())),
278 ConstantValue::True => write!(f, "true"),
279 ConstantValue::False => write!(f, "false"),
280 ConstantValue::Null => write!(f, "null"),
281 ConstantValue::Num(ConstantNumber(n)) => write!(f, "{n}"),
282 ConstantValue::BigInt(n) => write!(f, "{n}"),
283 ConstantValue::Regex(regex) => write!(f, "/{}/{}", regex.0, regex.1),
284 }
285 }
286}
287
288#[derive(Debug, Clone, Hash, PartialEq, Eq)]
289pub struct ModuleValue {
290 pub module: Wtf8Atom,
291 pub annotations: Option<Arc<ImportAnnotations>>,
292 pub analyze_for_constants: bool,
297 pub reference: Option<ModuleReferenceIndex>,
299}
300
301#[derive(Copy, Debug, Clone, Hash, PartialEq, Eq)]
302pub struct ModuleReferenceIndex(NonZeroU32);
303
304impl From<u32> for ModuleReferenceIndex {
305 fn from(value: u32) -> Self {
306 ModuleReferenceIndex(NonZeroU32::new(value + 1).unwrap())
309 }
310}
311impl ModuleReferenceIndex {
312 pub fn get(&self) -> usize {
313 (self.0.get() - 1) as usize
314 }
315}
316
317#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
318pub enum LogicalOperator {
319 And,
320 Or,
321 NullishCoalescing,
322}
323
324impl LogicalOperator {
325 pub(super) fn joiner(&self) -> &'static str {
326 match self {
327 LogicalOperator::And => " && ",
328 LogicalOperator::Or => " || ",
329 LogicalOperator::NullishCoalescing => " ?? ",
330 }
331 }
332 pub(super) fn multi_line_joiner(&self) -> &'static str {
333 match self {
334 LogicalOperator::And => "&& ",
335 LogicalOperator::Or => "|| ",
336 LogicalOperator::NullishCoalescing => "?? ",
337 }
338 }
339}
340
341#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
342pub enum BinaryOperator {
343 Equal,
344 NotEqual,
345 StrictEqual,
346 StrictNotEqual,
347}
348
349impl BinaryOperator {
350 pub(super) fn joiner(&self) -> &'static str {
351 match self {
352 BinaryOperator::Equal => " == ",
353 BinaryOperator::NotEqual => " != ",
354 BinaryOperator::StrictEqual => " === ",
355 BinaryOperator::StrictNotEqual => " !== ",
356 }
357 }
358
359 pub(super) fn positive_op(&self) -> (PositiveBinaryOperator, bool) {
360 match self {
361 BinaryOperator::Equal => (PositiveBinaryOperator::Equal, false),
362 BinaryOperator::NotEqual => (PositiveBinaryOperator::Equal, true),
363 BinaryOperator::StrictEqual => (PositiveBinaryOperator::StrictEqual, false),
364 BinaryOperator::StrictNotEqual => (PositiveBinaryOperator::StrictEqual, true),
365 }
366 }
367}
368
369#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
370pub enum PositiveBinaryOperator {
371 Equal,
372 StrictEqual,
373}
374
375#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
376pub enum JsValueUrlKind {
377 Absolute,
378 Relative,
379}
380
381impl Display for JsValueUrlKind {
382 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
383 f.write_str(match self {
384 JsValueUrlKind::Absolute => "absolute",
385 JsValueUrlKind::Relative => "relative",
386 })
387 }
388}
389
390pub(super) enum JsValueMetaKind {
392 Leaf,
394 Nested,
397 Operation,
400 Placeholder,
402}
403
404#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
405pub enum LogicalProperty {
406 Truthy,
407 Falsy,
408 Nullish,
409 NonNullish,
410}
411
412impl Display for LogicalProperty {
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 match self {
415 LogicalProperty::Truthy => write!(f, "truthy"),
416 LogicalProperty::Falsy => write!(f, "falsy"),
417 LogicalProperty::Nullish => write!(f, "nullish"),
418 LogicalProperty::NonNullish => write!(f, "non-nullish"),
419 }
420 }
421}
422
423#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
424pub enum ObjectMutability {
425 Frozen,
429 FrozenSubset,
432 Mutable,
435}
436
437impl ObjectMutability {
438 pub fn merge_with(&mut self, other: Self) {
439 *self = std::cmp::max(*self, other)
440 }
441
442 pub fn is_mutable(&self) -> bool {
443 matches!(self, ObjectMutability::Mutable)
444 }
445 pub fn is_missing_unknown(&self) -> bool {
446 matches!(self, ObjectMutability::FrozenSubset)
447 }
448}
449
450impl Display for ObjectMutability {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 match self {
453 ObjectMutability::Frozen => write!(f, "frozen"),
454 ObjectMutability::FrozenSubset => write!(f, "frozen subset"),
455 ObjectMutability::Mutable => write!(f, ""),
456 }
457 }
458}