1use std::{
2 borrow::Cow,
3 fmt::{self, Display, Formatter},
4 hash::{Hash, Hasher},
5};
6
7use is_macro::Is;
8use num_bigint::BigInt as BigIntValue;
9use swc_atoms::{Atom, Wtf8Atom};
10use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP};
11
12use crate::jsx::JSXText;
13
14#[ast_node]
15#[derive(Eq, Hash, EqIgnoreSpan, Is)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
18pub enum Lit {
19 #[tag("StringLiteral")]
20 Str(Str),
21
22 #[tag("BooleanLiteral")]
23 Bool(Bool),
24
25 #[tag("NullLiteral")]
26 Null(Null),
27
28 #[tag("NumericLiteral")]
29 Num(Number),
30
31 #[tag("BigIntLiteral")]
32 BigInt(BigInt),
33
34 #[tag("RegExpLiteral")]
35 Regex(Regex),
36
37 #[tag("JSXText")]
38 JSXText(JSXText),
39}
40
41macro_rules! bridge_lit_from {
42 ($bridge: ty, $src:ty) => {
43 bridge_expr_from!(crate::Lit, $src);
44 bridge_from!(Lit, $bridge, $src);
45 };
46}
47
48bridge_expr_from!(Lit, Str);
49bridge_expr_from!(Lit, Bool);
50bridge_expr_from!(Lit, Number);
51bridge_expr_from!(Lit, BigInt);
52bridge_expr_from!(Lit, Regex);
53bridge_expr_from!(Lit, Null);
54bridge_expr_from!(Lit, JSXText);
55
56bridge_lit_from!(Str, &'_ str);
57bridge_lit_from!(Str, Atom);
58bridge_lit_from!(Str, Wtf8Atom);
59bridge_lit_from!(Str, Cow<'_, str>);
60bridge_lit_from!(Str, String);
61bridge_lit_from!(Bool, bool);
62bridge_lit_from!(Number, f64);
63bridge_lit_from!(Number, usize);
64bridge_lit_from!(BigInt, BigIntValue);
65
66impl Lit {
67 pub fn set_span(&mut self, span: Span) {
68 match self {
69 Lit::Str(s) => s.span = span,
70 Lit::Bool(b) => b.span = span,
71 Lit::Null(n) => n.span = span,
72 Lit::Num(n) => n.span = span,
73 Lit::BigInt(n) => n.span = span,
74 Lit::Regex(n) => n.span = span,
75 Lit::JSXText(n) => n.span = span,
76 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
77 _ => swc_common::unknown!(),
78 }
79 }
80}
81
82#[ast_node("BigIntLiteral")]
83#[derive(Eq, Hash)]
84pub struct BigInt {
85 pub span: Span,
86 #[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = EncodeBigInt))]
87 #[cfg_attr(feature = "encoding-impl", encoding(with = "EncodeBigInt2"))]
88 pub value: Box<BigIntValue>,
89
90 #[cfg_attr(
93 feature = "encoding-impl",
94 encoding(with = "cbor4ii::core::types::Maybe")
95 )]
96 pub raw: Option<Atom>,
97}
98
99#[cfg(feature = "shrink-to-fit")]
100impl shrink_to_fit::ShrinkToFit for BigInt {
101 #[inline(always)]
102 fn shrink_to_fit(&mut self) {}
103}
104
105impl EqIgnoreSpan for BigInt {
106 fn eq_ignore_span(&self, other: &Self) -> bool {
107 self.value == other.value
108 }
109}
110
111#[cfg(feature = "encoding-impl")]
112struct EncodeBigInt2<T>(T);
113
114#[cfg(feature = "encoding-impl")]
115impl cbor4ii::core::enc::Encode for EncodeBigInt2<&'_ Box<BigIntValue>> {
116 #[inline]
117 fn encode<W: cbor4ii::core::enc::Write>(
118 &self,
119 writer: &mut W,
120 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
121 cbor4ii::core::types::Bytes(self.0.to_signed_bytes_le().as_slice()).encode(writer)
122 }
123}
124
125#[cfg(feature = "encoding-impl")]
126impl<'de> cbor4ii::core::dec::Decode<'de> for EncodeBigInt2<Box<BigIntValue>> {
127 #[inline]
128 fn decode<R: cbor4ii::core::dec::Read<'de>>(
129 reader: &mut R,
130 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
131 let buf = <cbor4ii::core::types::Bytes<&'de [u8]>>::decode(reader)?;
132 Ok(EncodeBigInt2(Box::new(BigIntValue::from_signed_bytes_le(
133 buf.0,
134 ))))
135 }
136}
137
138#[cfg(feature = "rkyv-impl")]
139#[derive(Debug, Clone, Copy)]
140#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
141#[cfg_attr(feature = "rkyv-impl", repr(C))]
142pub struct EncodeBigInt;
143
144#[cfg(feature = "rkyv-impl")]
145impl rkyv::with::ArchiveWith<Box<BigIntValue>> for EncodeBigInt {
146 type Archived = rkyv::Archived<String>;
147 type Resolver = rkyv::Resolver<String>;
148
149 fn resolve_with(
150 field: &Box<BigIntValue>,
151 resolver: Self::Resolver,
152 out: rkyv::Place<Self::Archived>,
153 ) {
154 use rkyv::Archive;
155
156 let s = field.to_string();
157 s.resolve(resolver, out);
158 }
159}
160
161#[cfg(feature = "rkyv-impl")]
162impl<S> rkyv::with::SerializeWith<Box<BigIntValue>, S> for EncodeBigInt
163where
164 S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
165 S::Error: rancor::Source,
166{
167 fn serialize_with(
168 field: &Box<BigIntValue>,
169 serializer: &mut S,
170 ) -> Result<Self::Resolver, S::Error> {
171 let field = field.to_string();
172 rkyv::string::ArchivedString::serialize_from_str(&field, serializer)
173 }
174}
175
176#[cfg(feature = "rkyv-impl")]
177impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Box<BigIntValue>, D> for EncodeBigInt
178where
179 D: ?Sized + rancor::Fallible,
180{
181 fn deserialize_with(
182 field: &rkyv::Archived<String>,
183 deserializer: &mut D,
184 ) -> Result<Box<BigIntValue>, D::Error> {
185 use rkyv::Deserialize;
186
187 let s: String = field.deserialize(deserializer)?;
188
189 Ok(Box::new(s.parse().unwrap()))
190 }
191}
192
193#[cfg(feature = "arbitrary")]
194#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
195impl<'a> arbitrary::Arbitrary<'a> for BigInt {
196 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
197 let span = u.arbitrary()?;
198 let value = Box::new(u.arbitrary::<usize>()?.into());
199 let raw = Some(u.arbitrary::<String>()?.into());
200
201 Ok(Self { span, value, raw })
202 }
203}
204
205impl From<BigIntValue> for BigInt {
206 #[inline]
207 fn from(value: BigIntValue) -> Self {
208 BigInt {
209 span: DUMMY_SP,
210 value: Box::new(value),
211 raw: None,
212 }
213 }
214}
215
216#[ast_node("StringLiteral")]
218#[derive(Eq, Hash)]
219#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
220pub struct Str {
221 pub span: Span,
222
223 pub value: Wtf8Atom,
224
225 #[cfg_attr(
228 feature = "encoding-impl",
229 encoding(with = "cbor4ii::core::types::Maybe")
230 )]
231 pub raw: Option<Atom>,
232}
233
234impl Take for Str {
235 fn dummy() -> Self {
236 Str {
237 span: DUMMY_SP,
238 value: Wtf8Atom::default(),
239 raw: None,
240 }
241 }
242}
243
244#[cfg(feature = "arbitrary")]
245#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
246impl<'a> arbitrary::Arbitrary<'a> for Str {
247 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
248 let span = u.arbitrary()?;
249 let value = u.arbitrary::<Wtf8Atom>()?.into();
250 let raw = Some(u.arbitrary::<String>()?.into());
251
252 Ok(Self { span, value, raw })
253 }
254}
255
256impl Str {
257 #[inline]
258 pub fn is_empty(&self) -> bool {
259 self.value.is_empty()
260 }
261
262 pub fn from_tpl_raw(tpl_raw: &str) -> Atom {
263 let mut buf = String::with_capacity(tpl_raw.len());
264
265 let mut iter = tpl_raw.chars();
266
267 while let Some(c) = iter.next() {
268 match c {
269 '\\' => {
270 if let Some(next) = iter.next() {
271 match next {
272 '`' | '$' | '\\' => {
273 buf.push(next);
274 }
275 'b' => {
276 buf.push('\u{0008}');
277 }
278 'f' => {
279 buf.push('\u{000C}');
280 }
281 'n' => {
282 buf.push('\n');
283 }
284 'r' => {
285 buf.push('\r');
286 }
287 't' => {
288 buf.push('\t');
289 }
290 'v' => {
291 buf.push('\u{000B}');
292 }
293 _ => {
294 buf.push('\\');
295 buf.push(next);
296 }
297 }
298 }
299 }
300
301 c => {
302 buf.push(c);
303 }
304 }
305 }
306
307 buf.into()
308 }
309}
310
311impl EqIgnoreSpan for Str {
312 fn eq_ignore_span(&self, other: &Self) -> bool {
313 self.value == other.value
314 }
315}
316
317impl From<Atom> for Str {
318 #[inline]
319 fn from(value: Atom) -> Self {
320 Str {
321 span: DUMMY_SP,
322 value: value.into(),
323 raw: None,
324 }
325 }
326}
327
328impl From<Wtf8Atom> for Str {
329 #[inline]
330 fn from(value: Wtf8Atom) -> Self {
331 Str {
332 span: DUMMY_SP,
333 value,
334 raw: None,
335 }
336 }
337}
338
339bridge_from!(Str, Atom, &'_ str);
340bridge_from!(Str, Atom, String);
341bridge_from!(Str, Atom, Cow<'_, str>);
342
343#[ast_node("BooleanLiteral")]
353#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
354#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
355#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
356pub struct Bool {
357 pub span: Span,
358 pub value: bool,
359}
360
361impl Take for Bool {
362 fn dummy() -> Self {
363 Bool {
364 span: DUMMY_SP,
365 value: false,
366 }
367 }
368}
369
370impl From<bool> for Bool {
371 #[inline]
372 fn from(value: bool) -> Self {
373 Bool {
374 span: DUMMY_SP,
375 value,
376 }
377 }
378}
379
380#[ast_node("NullLiteral")]
381#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
382#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
383#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
384pub struct Null {
385 pub span: Span,
386}
387
388impl Take for Null {
389 fn dummy() -> Self {
390 Null { span: DUMMY_SP }
391 }
392}
393
394#[ast_node("RegExpLiteral")]
395#[derive(Eq, Hash, EqIgnoreSpan)]
396#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
397pub struct Regex {
398 pub span: Span,
399
400 #[cfg_attr(feature = "serde-impl", serde(rename = "pattern"))]
401 pub exp: Atom,
402
403 #[cfg_attr(feature = "serde-impl", serde(default))]
404 pub flags: Atom,
405}
406
407impl Take for Regex {
408 fn dummy() -> Self {
409 Self {
410 span: DUMMY_SP,
411 exp: Default::default(),
412 flags: Default::default(),
413 }
414 }
415}
416
417#[cfg(feature = "arbitrary")]
418#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
419impl<'a> arbitrary::Arbitrary<'a> for Regex {
420 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
421 use swc_atoms::atom;
422
423 let span = u.arbitrary()?;
424 let exp = u.arbitrary::<String>()?.into();
425 let flags = atom!(""); Ok(Self { span, exp, flags })
428 }
429}
430
431#[ast_node("NumericLiteral")]
443#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
444pub struct Number {
445 pub span: Span,
446 pub value: f64,
450
451 #[cfg_attr(
454 feature = "encoding-impl",
455 encoding(with = "cbor4ii::core::types::Maybe")
456 )]
457 pub raw: Option<Atom>,
458}
459
460impl Eq for Number {}
461
462impl EqIgnoreSpan for Number {
463 fn eq_ignore_span(&self, other: &Self) -> bool {
464 self.value == other.value && self.value.is_sign_positive() == other.value.is_sign_positive()
465 }
466}
467
468#[allow(clippy::derived_hash_with_manual_eq)]
469#[allow(clippy::transmute_float_to_int)]
470impl Hash for Number {
471 fn hash<H: Hasher>(&self, state: &mut H) {
472 fn integer_decode(val: f64) -> (u64, i16, i8) {
473 let bits: u64 = val.to_bits();
474 let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
475 let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
476 let mantissa = if exponent == 0 {
477 (bits & 0xfffffffffffff) << 1
478 } else {
479 (bits & 0xfffffffffffff) | 0x10000000000000
480 };
481
482 exponent -= 1023 + 52;
483 (mantissa, exponent, sign)
484 }
485
486 self.span.hash(state);
487 integer_decode(self.value).hash(state);
488 self.raw.hash(state);
489 }
490}
491
492impl Display for Number {
493 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
494 if self.value.is_infinite() {
495 if self.value.is_sign_positive() {
496 Display::fmt("Infinity", f)
497 } else {
498 Display::fmt("-Infinity", f)
499 }
500 } else {
501 Display::fmt(&self.value, f)
502 }
503 }
504}
505
506#[cfg(feature = "arbitrary")]
507#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
508impl<'a> arbitrary::Arbitrary<'a> for Number {
509 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
510 let span = u.arbitrary()?;
511 let value = u.arbitrary::<f64>()?;
512 let raw = Some(u.arbitrary::<String>()?.into());
513
514 Ok(Self { span, value, raw })
515 }
516}
517
518impl From<f64> for Number {
519 #[inline]
520 fn from(value: f64) -> Self {
521 Number {
522 span: DUMMY_SP,
523 value,
524 raw: None,
525 }
526 }
527}
528
529impl From<usize> for Number {
530 #[inline]
531 fn from(value: usize) -> Self {
532 Number {
533 span: DUMMY_SP,
534 value: value as _,
535 raw: None,
536 }
537 }
538}