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, trace::TraceRawVcs};
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, TraceRawVcs)]
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, TraceRawVcs)]
77pub enum ConstantString {
78 Atom(#[turbo_tasks(trace_ignore)] 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, TraceRawVcs, Encode, Decode)]
170pub enum ConstantValue {
171 #[default]
172 Undefined,
173 Str(ConstantString),
174 Num(ConstantNumber),
175 True,
176 False,
177 Null,
178 BigInt(
179 #[turbo_tasks(trace_ignore)]
180 #[bincode(with_serde)]
181 Box<BigInt>,
182 ),
183 Regex(
184 #[turbo_tasks(trace_ignore)]
185 #[bincode(with_serde)]
186 Box<(Atom, Atom)>,
187 ),
188}
189unsafe impl NonLocalValue for ConstantValue {}
190
191impl ConstantValue {
192 pub fn as_str(&self) -> Option<&str> {
193 match self {
194 Self::Str(s) => Some(s.as_str()),
195 _ => None,
196 }
197 }
198
199 pub fn as_bool(&self) -> Option<bool> {
200 match self {
201 Self::True => Some(true),
202 Self::False => Some(false),
203 _ => None,
204 }
205 }
206
207 pub fn is_truthy(&self) -> bool {
208 match self {
209 Self::Undefined | Self::False | Self::Null => false,
210 Self::True | Self::Regex(..) => true,
211 Self::Str(s) => !s.is_empty(),
212 Self::Num(ConstantNumber(n)) => *n != 0.0,
213 Self::BigInt(n) => !n.is_zero(),
214 }
215 }
216
217 pub fn is_nullish(&self) -> bool {
218 match self {
219 Self::Undefined | Self::Null => true,
220 Self::Str(..)
221 | Self::Num(..)
222 | Self::True
223 | Self::False
224 | Self::BigInt(..)
225 | Self::Regex(..) => false,
226 }
227 }
228
229 pub fn is_empty_string(&self) -> bool {
230 match self {
231 Self::Str(s) => s.is_empty(),
232 _ => false,
233 }
234 }
235
236 pub fn is_value_type(&self) -> bool {
237 !matches!(self, Self::Regex(..))
238 }
239}
240
241impl From<bool> for ConstantValue {
242 fn from(v: bool) -> Self {
243 match v {
244 true => ConstantValue::True,
245 false => ConstantValue::False,
246 }
247 }
248}
249
250impl From<&'_ str> for ConstantValue {
251 fn from(v: &str) -> Self {
252 ConstantValue::Str(ConstantString::Atom(v.into()))
253 }
254}
255
256impl From<Lit> for ConstantValue {
257 fn from(v: Lit) -> Self {
258 match v {
259 Lit::Str(v) => {
260 ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned()))
261 }
262 Lit::Bool(v) => {
263 if v.value {
264 ConstantValue::True
265 } else {
266 ConstantValue::False
267 }
268 }
269 Lit::Null(_) => ConstantValue::Null,
270 Lit::Num(v) => ConstantValue::Num(ConstantNumber(v.value)),
271 Lit::BigInt(v) => ConstantValue::BigInt(v.value),
272 Lit::Regex(v) => ConstantValue::Regex(Box::new((v.exp, v.flags))),
273 Lit::JSXText(v) => {
274 ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned()))
276 }
277 }
278 }
279}
280
281impl Display for ConstantValue {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 match self {
284 ConstantValue::Undefined => write!(f, "undefined"),
285 ConstantValue::Str(str) => write!(f, "{}", StringifyJs(str.as_str())),
286 ConstantValue::True => write!(f, "true"),
287 ConstantValue::False => write!(f, "false"),
288 ConstantValue::Null => write!(f, "null"),
289 ConstantValue::Num(ConstantNumber(n)) => write!(f, "{n}"),
290 ConstantValue::BigInt(n) => write!(f, "{n}"),
291 ConstantValue::Regex(regex) => write!(f, "/{}/{}", regex.0, regex.1),
292 }
293 }
294}
295
296#[derive(Debug, Clone, Hash, PartialEq, Eq)]
297pub struct ModuleValue {
298 pub module: Wtf8Atom,
299 pub annotations: Option<Arc<ImportAnnotations>>,
300 pub analyze_for_constants: bool,
305 pub reference: Option<ModuleReferenceIndex>,
307}
308
309#[derive(Copy, Debug, Clone, Hash, PartialEq, Eq)]
310pub struct ModuleReferenceIndex(NonZeroU32);
311
312impl From<u32> for ModuleReferenceIndex {
313 fn from(value: u32) -> Self {
314 ModuleReferenceIndex(NonZeroU32::new(value + 1).unwrap())
317 }
318}
319impl ModuleReferenceIndex {
320 pub fn get(&self) -> usize {
321 (self.0.get() - 1) as usize
322 }
323}
324
325#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
326pub enum LogicalOperator {
327 And,
328 Or,
329 NullishCoalescing,
330}
331
332impl LogicalOperator {
333 pub(super) fn joiner(&self) -> &'static str {
334 match self {
335 LogicalOperator::And => " && ",
336 LogicalOperator::Or => " || ",
337 LogicalOperator::NullishCoalescing => " ?? ",
338 }
339 }
340 pub(super) fn multi_line_joiner(&self) -> &'static str {
341 match self {
342 LogicalOperator::And => "&& ",
343 LogicalOperator::Or => "|| ",
344 LogicalOperator::NullishCoalescing => "?? ",
345 }
346 }
347}
348
349#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
350pub enum BinaryOperator {
351 Equal,
352 NotEqual,
353 StrictEqual,
354 StrictNotEqual,
355}
356
357impl BinaryOperator {
358 pub(super) fn joiner(&self) -> &'static str {
359 match self {
360 BinaryOperator::Equal => " == ",
361 BinaryOperator::NotEqual => " != ",
362 BinaryOperator::StrictEqual => " === ",
363 BinaryOperator::StrictNotEqual => " !== ",
364 }
365 }
366
367 pub(super) fn positive_op(&self) -> (PositiveBinaryOperator, bool) {
368 match self {
369 BinaryOperator::Equal => (PositiveBinaryOperator::Equal, false),
370 BinaryOperator::NotEqual => (PositiveBinaryOperator::Equal, true),
371 BinaryOperator::StrictEqual => (PositiveBinaryOperator::StrictEqual, false),
372 BinaryOperator::StrictNotEqual => (PositiveBinaryOperator::StrictEqual, true),
373 }
374 }
375}
376
377#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
378pub enum PositiveBinaryOperator {
379 Equal,
380 StrictEqual,
381}
382
383#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
384pub enum JsValueUrlKind {
385 Absolute,
386 Relative,
387}
388
389impl Display for JsValueUrlKind {
390 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391 f.write_str(match self {
392 JsValueUrlKind::Absolute => "absolute",
393 JsValueUrlKind::Relative => "relative",
394 })
395 }
396}
397
398pub(super) enum JsValueMetaKind {
400 Leaf,
402 Nested,
405 Operation,
408 Placeholder,
410}
411
412#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
413pub enum LogicalProperty {
414 Truthy,
415 Falsy,
416 Nullish,
417 NonNullish,
418}
419
420impl Display for LogicalProperty {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 match self {
423 LogicalProperty::Truthy => write!(f, "truthy"),
424 LogicalProperty::Falsy => write!(f, "falsy"),
425 LogicalProperty::Nullish => write!(f, "nullish"),
426 LogicalProperty::NonNullish => write!(f, "non-nullish"),
427 }
428 }
429}
430
431#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
432pub enum ObjectMutability {
433 Frozen,
437 FrozenSubset,
440 Mutable,
443}
444
445impl ObjectMutability {
446 pub fn merge_with(&mut self, other: Self) {
447 *self = std::cmp::max(*self, other)
448 }
449
450 pub fn is_mutable(&self) -> bool {
451 matches!(self, ObjectMutability::Mutable)
452 }
453 pub fn is_missing_unknown(&self) -> bool {
454 matches!(self, ObjectMutability::FrozenSubset)
455 }
456}
457
458impl Display for ObjectMutability {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 match self {
461 ObjectMutability::Frozen => write!(f, "frozen"),
462 ObjectMutability::FrozenSubset => write!(f, "frozen subset"),
463 ObjectMutability::Mutable => write!(f, ""),
464 }
465 }
466}