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::{
10 wtf8::{CodePoint, Wtf8Buf},
11 Atom, Wtf8Atom,
12};
13use swc_common::{ast_node, errors::HANDLER, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP};
14
15use crate::{jsx::JSXText, TplElement};
16
17#[ast_node]
18#[derive(Eq, Hash, EqIgnoreSpan, Is)]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
21pub enum Lit {
22 #[tag("StringLiteral")]
23 Str(Str),
24
25 #[tag("BooleanLiteral")]
26 Bool(Bool),
27
28 #[tag("NullLiteral")]
29 Null(Null),
30
31 #[tag("NumericLiteral")]
32 Num(Number),
33
34 #[tag("BigIntLiteral")]
35 BigInt(BigInt),
36
37 #[tag("RegExpLiteral")]
38 Regex(Regex),
39
40 #[tag("JSXText")]
41 JSXText(JSXText),
42}
43
44macro_rules! bridge_lit_from {
45 ($bridge: ty, $src:ty) => {
46 bridge_expr_from!(crate::Lit, $src);
47 bridge_from!(Lit, $bridge, $src);
48 };
49}
50
51bridge_expr_from!(Lit, Str);
52bridge_expr_from!(Lit, Bool);
53bridge_expr_from!(Lit, Number);
54bridge_expr_from!(Lit, BigInt);
55bridge_expr_from!(Lit, Regex);
56bridge_expr_from!(Lit, Null);
57bridge_expr_from!(Lit, JSXText);
58
59bridge_lit_from!(Str, &'_ str);
60bridge_lit_from!(Str, Atom);
61bridge_lit_from!(Str, Wtf8Atom);
62bridge_lit_from!(Str, Cow<'_, str>);
63bridge_lit_from!(Str, String);
64bridge_lit_from!(Bool, bool);
65bridge_lit_from!(Number, f64);
66bridge_lit_from!(Number, usize);
67bridge_lit_from!(BigInt, BigIntValue);
68
69impl Lit {
70 pub fn set_span(&mut self, span: Span) {
71 match self {
72 Lit::Str(s) => s.span = span,
73 Lit::Bool(b) => b.span = span,
74 Lit::Null(n) => n.span = span,
75 Lit::Num(n) => n.span = span,
76 Lit::BigInt(n) => n.span = span,
77 Lit::Regex(n) => n.span = span,
78 Lit::JSXText(n) => n.span = span,
79 #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
80 _ => swc_common::unknown!(),
81 }
82 }
83}
84
85#[ast_node("BigIntLiteral")]
86#[derive(Eq, Hash)]
87pub struct BigInt {
88 pub span: Span,
89 #[cfg_attr(feature = "encoding-impl", encoding(with = "EncodeBigInt2"))]
90 pub value: Box<BigIntValue>,
91
92 #[cfg_attr(
95 feature = "encoding-impl",
96 encoding(with = "cbor4ii::core::types::Maybe")
97 )]
98 pub raw: Option<Atom>,
99}
100
101#[cfg(feature = "shrink-to-fit")]
102impl shrink_to_fit::ShrinkToFit for BigInt {
103 #[inline(always)]
104 fn shrink_to_fit(&mut self) {}
105}
106
107impl EqIgnoreSpan for BigInt {
108 fn eq_ignore_span(&self, other: &Self) -> bool {
109 self.value == other.value
110 }
111}
112
113#[cfg(feature = "encoding-impl")]
114struct EncodeBigInt2<T>(T);
115
116#[cfg(feature = "encoding-impl")]
117impl cbor4ii::core::enc::Encode for EncodeBigInt2<&'_ Box<BigIntValue>> {
118 #[inline]
119 fn encode<W: cbor4ii::core::enc::Write>(
120 &self,
121 writer: &mut W,
122 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
123 cbor4ii::core::types::Bytes(self.0.to_signed_bytes_le().as_slice()).encode(writer)
124 }
125}
126
127#[cfg(feature = "encoding-impl")]
128impl<'de> cbor4ii::core::dec::Decode<'de> for EncodeBigInt2<Box<BigIntValue>> {
129 #[inline]
130 fn decode<R: cbor4ii::core::dec::Read<'de>>(
131 reader: &mut R,
132 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
133 let buf = <cbor4ii::core::types::Bytes<&'de [u8]>>::decode(reader)?;
134 Ok(EncodeBigInt2(Box::new(BigIntValue::from_signed_bytes_le(
135 buf.0,
136 ))))
137 }
138}
139
140#[cfg(feature = "arbitrary")]
141#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
142impl<'a> arbitrary::Arbitrary<'a> for BigInt {
143 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
144 let span = u.arbitrary()?;
145 let value = Box::new(u.arbitrary::<usize>()?.into());
146 let raw = Some(u.arbitrary::<String>()?.into());
147
148 Ok(Self { span, value, raw })
149 }
150}
151
152impl From<BigIntValue> for BigInt {
153 #[inline]
154 fn from(value: BigIntValue) -> Self {
155 BigInt {
156 span: DUMMY_SP,
157 value: Box::new(value),
158 raw: None,
159 }
160 }
161}
162
163#[ast_node("StringLiteral")]
165#[derive(Eq, Hash)]
166#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
167pub struct Str {
168 pub span: Span,
169
170 pub value: Wtf8Atom,
171
172 #[cfg_attr(
175 feature = "encoding-impl",
176 encoding(with = "cbor4ii::core::types::Maybe")
177 )]
178 pub raw: Option<Atom>,
179}
180
181impl Take for Str {
182 fn dummy() -> Self {
183 Str {
184 span: DUMMY_SP,
185 value: Wtf8Atom::default(),
186 raw: None,
187 }
188 }
189}
190
191#[cfg(feature = "arbitrary")]
192#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
193impl<'a> arbitrary::Arbitrary<'a> for Str {
194 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
195 let span = u.arbitrary()?;
196 let value = u.arbitrary::<Wtf8Atom>()?.into();
197 let raw = Some(u.arbitrary::<String>()?.into());
198
199 Ok(Self { span, value, raw })
200 }
201}
202
203fn emit_span_error(span: Span, msg: &str) {
204 HANDLER.with(|handler| {
205 handler.struct_span_err(span, msg).emit();
206 });
207}
208
209impl Str {
210 #[inline]
211 pub fn is_empty(&self) -> bool {
212 self.value.is_empty()
213 }
214
215 pub fn from_tpl_raw(tpl: &TplElement) -> Wtf8Atom {
221 let tpl_raw = &tpl.raw;
222 let span = tpl.span;
223 let mut buf: Wtf8Buf = Wtf8Buf::with_capacity(tpl_raw.len());
224 let mut iter = tpl_raw.chars();
225 while let Some(c) = iter.next() {
226 match c {
227 '\\' => {
228 if let Some(c) = iter.next() {
229 match c {
230 '`' | '$' | '\\' => {
231 buf.push_char(c);
232 }
233 'b' => {
234 buf.push_char('\u{0008}');
235 }
236 'f' => {
237 buf.push_char('\u{000C}');
238 }
239 'n' => {
240 buf.push_char('\n');
241 }
242 'r' => {
243 buf.push_char('\r');
244 }
245 't' => {
246 buf.push_char('\t');
247 }
248 'v' => {
249 buf.push_char('\u{000B}');
250 }
251 '\r' => {
252 let mut next_iter = iter.clone();
253 if let Some('\n') = next_iter.next() {
254 iter = next_iter;
255 }
256 }
257 '\n' | '\u{2028}' | '\u{2029}' => {}
258 'u' | 'x' => {
259 let braced = c == 'u' && iter.clone().next() == Some('{');
260 if braced {
261 iter.next();
262 }
263 let digits = if c == 'u' { 4 } else { 2 };
264 let mut count = 0;
265 let mut result = 0u32;
266 let mut terminated = !braced;
267 for c in &mut iter {
268 if braced && c == '}' {
269 terminated = true;
270 break;
271 }
272 let Some(digit) = c.to_digit(16) else {
273 emit_span_error(
274 span,
275 "Uncaught SyntaxError: Invalid Unicode escape sequence",
276 );
277 break;
278 };
279 result = result.saturating_mul(16).saturating_add(digit);
282 count += 1;
283 if !braced && count == digits {
284 break;
285 }
286 }
287 if !terminated || count == 0 || (!braced && count != digits) {
288 emit_span_error(
289 span,
290 "Uncaught SyntaxError: Invalid hexadecimal escape sequence",
291 );
292 }
293 if let Some(code_point) = CodePoint::from_u32(result) {
294 buf.push(code_point);
297 } else {
298 emit_span_error(
299 span,
300 "Uncaught SyntaxError: Undefined Unicode code-point",
301 );
302 }
303 }
304 '0'..='7' => {
305 let next = iter.clone().next();
306 if c == '0' {
307 match next {
308 Some(next) => {
309 if !next.is_digit(8) {
310 buf.push_char('\u{0000}');
311 continue;
312 }
313 }
314 _ => {
316 buf.push_char('\u{0000}');
317 continue;
318 }
319 }
320 }
321 emit_span_error(
322 span,
323 "Uncaught SyntaxError: Octal escape sequences are not allowed \
324 in template strings.",
325 );
326 }
327 _ => {
328 buf.push_char(c);
330 }
331 }
332 }
333 }
334
335 '\r' => {
336 if iter.clone().next() == Some('\n') {
337 iter.next();
338 }
339 buf.push_char('\n');
340 }
341
342 c => {
343 buf.push_char(c);
344 }
345 }
346 }
347
348 buf.into()
349 }
350}
351
352impl EqIgnoreSpan for Str {
353 fn eq_ignore_span(&self, other: &Self) -> bool {
354 self.value == other.value
355 }
356}
357
358impl From<Atom> for Str {
359 #[inline]
360 fn from(value: Atom) -> Self {
361 Str {
362 span: DUMMY_SP,
363 value: value.into(),
364 raw: None,
365 }
366 }
367}
368
369impl From<Wtf8Atom> for Str {
370 #[inline]
371 fn from(value: Wtf8Atom) -> Self {
372 Str {
373 span: DUMMY_SP,
374 value,
375 raw: None,
376 }
377 }
378}
379
380bridge_from!(Str, Atom, &'_ str);
381bridge_from!(Str, Atom, String);
382bridge_from!(Str, Atom, Cow<'_, str>);
383
384#[ast_node("BooleanLiteral")]
394#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
395#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
396#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
397pub struct Bool {
398 pub span: Span,
399 pub value: bool,
400}
401
402impl Take for Bool {
403 fn dummy() -> Self {
404 Bool {
405 span: DUMMY_SP,
406 value: false,
407 }
408 }
409}
410
411impl From<bool> for Bool {
412 #[inline]
413 fn from(value: bool) -> Self {
414 Bool {
415 span: DUMMY_SP,
416 value,
417 }
418 }
419}
420
421#[ast_node("NullLiteral")]
422#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
423#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
424#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
425pub struct Null {
426 pub span: Span,
427}
428
429impl Take for Null {
430 fn dummy() -> Self {
431 Null { span: DUMMY_SP }
432 }
433}
434
435#[ast_node("RegExpLiteral")]
436#[derive(Eq, Hash, EqIgnoreSpan)]
437#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
438pub struct Regex {
439 pub span: Span,
440
441 #[cfg_attr(feature = "serde-impl", serde(rename = "pattern"))]
442 pub exp: Atom,
443
444 #[cfg_attr(feature = "serde-impl", serde(default))]
445 pub flags: Atom,
446}
447
448impl Take for Regex {
449 fn dummy() -> Self {
450 Self {
451 span: DUMMY_SP,
452 exp: Default::default(),
453 flags: Default::default(),
454 }
455 }
456}
457
458#[cfg(feature = "arbitrary")]
459#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
460impl<'a> arbitrary::Arbitrary<'a> for Regex {
461 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
462 use swc_atoms::atom;
463
464 let span = u.arbitrary()?;
465 let exp = u.arbitrary::<String>()?.into();
466 let flags = atom!(""); Ok(Self { span, exp, flags })
469 }
470}
471
472#[ast_node("NumericLiteral", no_partial_eq)]
484#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
485pub struct Number {
486 pub span: Span,
487 pub value: f64,
492
493 #[cfg_attr(
496 feature = "encoding-impl",
497 encoding(with = "cbor4ii::core::types::Maybe")
498 )]
499 pub raw: Option<Atom>,
500}
501
502impl PartialEq for Number {
503 fn eq(&self, other: &Self) -> bool {
504 self.span == other.span && number_value_eq(self.value, other.value) && self.raw == other.raw
505 }
506}
507
508impl Eq for Number {}
509
510impl EqIgnoreSpan for Number {
511 fn eq_ignore_span(&self, other: &Self) -> bool {
512 number_value_eq(self.value, other.value)
513 }
514}
515
516impl Hash for Number {
517 fn hash<H: Hasher>(&self, state: &mut H) {
518 self.span.hash(state);
519 canonical_number_bits(self.value).hash(state);
520 self.raw.hash(state);
521 }
522}
523
524#[inline]
528fn number_value_eq(left: f64, right: f64) -> bool {
529 left.to_bits() == right.to_bits() || (left.is_nan() && right.is_nan())
531}
532
533#[inline]
538fn canonical_number_bits(value: f64) -> u64 {
539 const CANONICAL_NAN_BITS: u64 = 0x7ff8_0000_0000_0000;
540
541 if value.is_nan() {
542 CANONICAL_NAN_BITS
543 } else {
544 value.to_bits()
545 }
546}
547
548impl Display for Number {
549 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
550 if self.value.is_infinite() {
551 if self.value.is_sign_positive() {
552 Display::fmt("Infinity", f)
553 } else {
554 Display::fmt("-Infinity", f)
555 }
556 } else {
557 Display::fmt(&self.value, f)
558 }
559 }
560}
561
562#[cfg(feature = "arbitrary")]
563#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
564impl<'a> arbitrary::Arbitrary<'a> for Number {
565 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
566 let span = u.arbitrary()?;
567 let value = u.arbitrary::<f64>()?;
568 let raw = Some(u.arbitrary::<String>()?.into());
569
570 Ok(Self { span, value, raw })
571 }
572}
573
574impl From<f64> for Number {
575 #[inline]
576 fn from(value: f64) -> Self {
577 Number {
578 span: DUMMY_SP,
579 value,
580 raw: None,
581 }
582 }
583}
584
585impl From<usize> for Number {
586 #[inline]
587 fn from(value: usize) -> Self {
588 Number {
589 span: DUMMY_SP,
590 value: value as _,
591 raw: None,
592 }
593 }
594}