use std::{ borrow::Cow, fmt::{self, Display, Formatter}, hash::{Hash, Hasher}, }; use num_bigint::BigInt as BigIntValue; use swc_atoms::{js_word, Atom, JsWord}; use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP}; use crate::jsx::JSXText; #[ast_node] #[derive(Eq, Hash, EqIgnoreSpan)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub enum Lit { #[tag("StringLiteral")] Str(Str), #[tag("BooleanLiteral")] Bool(Bool), #[tag("NullLiteral")] Null(Null), #[tag("NumericLiteral")] Num(Number), #[tag("BigIntLiteral")] BigInt(BigInt), #[tag("RegExpLiteral")] Regex(Regex), #[tag("JSXText")] JSXText(JSXText), } macro_rules! bridge_lit_from { ($bridge: ty, $src:ty) => { bridge_expr_from!(crate::Lit, $src); bridge_from!(Lit, $bridge, $src); }; } bridge_expr_from!(Lit, Str); bridge_expr_from!(Lit, Bool); bridge_expr_from!(Lit, Number); bridge_expr_from!(Lit, BigInt); bridge_expr_from!(Lit, Regex); bridge_expr_from!(Lit, Null); bridge_expr_from!(Lit, JSXText); bridge_lit_from!(Str, &'_ str); bridge_lit_from!(Str, JsWord); bridge_lit_from!(Str, Atom); bridge_lit_from!(Str, Cow<'_, str>); bridge_lit_from!(Str, String); bridge_lit_from!(Bool, bool); bridge_lit_from!(Number, f64); bridge_lit_from!(Number, usize); bridge_lit_from!(BigInt, BigIntValue); #[ast_node("BigIntLiteral")] #[derive(Eq, Hash)] pub struct BigInt { pub span: Span, #[cfg_attr(any(feature = "rkyv-impl"), with(EncodeBigInt))] pub value: Box, /// Use `None` value only for transformations to avoid recalculate /// characters in big integer pub raw: Option, } impl EqIgnoreSpan for BigInt { fn eq_ignore_span(&self, other: &Self) -> bool { self.value == other.value } } #[cfg(feature = "rkyv-impl")] #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "rkyv-impl", derive(rkyv::bytecheck::CheckBytes))] #[cfg_attr(feature = "rkyv-impl", repr(C))] pub struct EncodeBigInt; #[cfg(feature = "rkyv-impl")] impl rkyv::with::ArchiveWith> for EncodeBigInt { type Archived = rkyv::Archived; type Resolver = rkyv::Resolver; unsafe fn resolve_with( field: &Box, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived, ) { use rkyv::Archive; let s = field.to_string(); s.resolve(pos, resolver, out); } } #[cfg(feature = "rkyv-impl")] impl rkyv::with::SerializeWith, S> for EncodeBigInt where S: ?Sized + rkyv::ser::Serializer, { fn serialize_with( field: &Box, serializer: &mut S, ) -> Result { let field = field.to_string(); rkyv::string::ArchivedString::serialize_from_str(&field, serializer) } } #[cfg(feature = "rkyv-impl")] impl rkyv::with::DeserializeWith, Box, D> for EncodeBigInt where D: ?Sized + rkyv::Fallible, { fn deserialize_with( field: &rkyv::Archived, deserializer: &mut D, ) -> Result, D::Error> { use rkyv::Deserialize; let s: String = field.deserialize(deserializer)?; Ok(Box::new(s.parse().unwrap())) } } #[cfg(feature = "arbitrary")] #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] impl<'a> arbitrary::Arbitrary<'a> for BigInt { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { let span = u.arbitrary()?; let value = Box::new(u.arbitrary::()?.into()); let raw = Some(u.arbitrary::()?.into()); Ok(Self { span, value, raw }) } } impl From for BigInt { #[inline] fn from(value: BigIntValue) -> Self { BigInt { span: DUMMY_SP, value: Box::new(value), raw: None, } } } /// A string literal. #[ast_node("StringLiteral")] #[derive(Eq, Hash)] pub struct Str { pub span: Span, #[cfg_attr(any(feature = "rkyv-impl"), with(swc_atoms::EncodeJsWord))] pub value: JsWord, /// Use `None` value only for transformations to avoid recalculate escaped /// characters in strings pub raw: Option, } impl Take for Str { fn dummy() -> Self { Str { span: DUMMY_SP, value: js_word!(""), raw: None, } } } #[cfg(feature = "arbitrary")] #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] impl<'a> arbitrary::Arbitrary<'a> for Str { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { let span = u.arbitrary()?; let value = u.arbitrary::()?.into(); let raw = Some(u.arbitrary::()?.into()); Ok(Self { span, value, raw }) } } impl Str { #[inline] pub fn is_empty(&self) -> bool { self.value.is_empty() } } impl EqIgnoreSpan for Str { fn eq_ignore_span(&self, other: &Self) -> bool { self.value == other.value } } impl From for Str { #[inline] fn from(value: JsWord) -> Self { Str { span: DUMMY_SP, value, raw: None, } } } impl From for Str { #[inline] fn from(value: Atom) -> Self { Str { span: DUMMY_SP, value: JsWord::from(&*value), raw: None, } } } bridge_from!(Str, JsWord, &'_ str); bridge_from!(Str, JsWord, String); bridge_from!(Str, JsWord, Cow<'_, str>); /// A boolean literal. /// /// /// # Creation /// /// If you are creating a boolean literal with a dummy span, please use /// `true.into()` or `false.into()`, instead of creating this struct directly. /// /// All of `Box`, `Expr`, `Lit`, `Bool` implements `From`. #[ast_node("BooleanLiteral")] #[derive(Copy, Eq, Hash, EqIgnoreSpan)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct Bool { pub span: Span, pub value: bool, } impl Take for Bool { fn dummy() -> Self { Bool { span: DUMMY_SP, value: false, } } } impl From for Bool { #[inline] fn from(value: bool) -> Self { Bool { span: DUMMY_SP, value, } } } #[ast_node("NullLiteral")] #[derive(Copy, Eq, Hash, EqIgnoreSpan)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct Null { pub span: Span, } impl Take for Null { fn dummy() -> Self { Null { span: DUMMY_SP } } } #[ast_node("RegExpLiteral")] #[derive(Eq, Hash, EqIgnoreSpan)] pub struct Regex { pub span: Span, #[cfg_attr(feature = "serde-impl", serde(rename = "pattern"))] pub exp: Atom, #[cfg_attr(feature = "serde-impl", serde(default))] pub flags: Atom, } impl Take for Regex { fn dummy() -> Self { Self { span: DUMMY_SP, exp: Default::default(), flags: Default::default(), } } } #[cfg(feature = "arbitrary")] #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] impl<'a> arbitrary::Arbitrary<'a> for Regex { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { let span = u.arbitrary()?; let exp = u.arbitrary::()?.into(); let flags = "".into(); // TODO Ok(Self { span, exp, flags }) } } /// A numeric literal. /// /// /// # Creation /// /// If you are creating a numeric literal with a dummy span, please use /// `literal.into()`, instead of creating this struct directly. /// /// All of `Box`, `Expr`, `Lit`, `Number` implements `From<64>` and /// `From`. #[ast_node("NumericLiteral")] pub struct Number { pub span: Span, /// **Note**: This should not be `NaN`. Use [crate::Ident] to represent NaN. /// /// If you store `NaN` in this field, a hash map will behave strangely. pub value: f64, /// Use `None` value only for transformations to avoid recalculate /// characters in number literal pub raw: Option, } impl Eq for Number {} impl EqIgnoreSpan for Number { fn eq_ignore_span(&self, other: &Self) -> bool { self.value == other.value && self.value.is_sign_positive() == other.value.is_sign_positive() } } #[allow(clippy::derived_hash_with_manual_eq)] #[allow(clippy::transmute_float_to_int)] impl Hash for Number { fn hash(&self, state: &mut H) { fn integer_decode(val: f64) -> (u64, i16, i8) { let bits: u64 = val.to_bits(); let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; let mantissa = if exponent == 0 { (bits & 0xfffffffffffff) << 1 } else { (bits & 0xfffffffffffff) | 0x10000000000000 }; exponent -= 1023 + 52; (mantissa, exponent, sign) } self.span.hash(state); integer_decode(self.value).hash(state); self.raw.hash(state); } } impl Display for Number { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if self.value.is_infinite() { if self.value.is_sign_positive() { Display::fmt("Infinity", f) } else { Display::fmt("-Infinity", f) } } else { Display::fmt(&self.value, f) } } } #[cfg(feature = "arbitrary")] #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))] impl<'a> arbitrary::Arbitrary<'a> for Number { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { let span = u.arbitrary()?; let value = u.arbitrary::()?; let raw = Some(u.arbitrary::()?.into()); Ok(Self { span, value, raw }) } } impl From for Number { #[inline] fn from(value: f64) -> Self { Number { span: DUMMY_SP, value, raw: None, } } } impl From for Number { #[inline] fn from(value: usize) -> Self { Number { span: DUMMY_SP, value: value as _, raw: None, } } }