oxc_ecmascript/
to_big_int.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use num_bigint::BigInt;
use num_traits::{One, Zero};

use oxc_ast::ast::{BigIntLiteral, Expression};
use oxc_syntax::operator::UnaryOperator;

use crate::{StringToBigInt, ToBoolean, ToJsString};

/// `ToBigInt`
///
/// <https://tc39.es/ecma262/#sec-tobigint>
pub trait ToBigInt<'a> {
    fn to_big_int(&self) -> Option<BigInt>;
}

impl<'a> ToBigInt<'a> for Expression<'a> {
    #[expect(clippy::cast_possible_truncation)]
    fn to_big_int(&self) -> Option<BigInt> {
        match self {
            Expression::NumericLiteral(number_literal) => {
                let value = number_literal.value;
                if value.abs() < 2_f64.powi(53) && value.fract() == 0.0 {
                    Some(BigInt::from(value as i64))
                } else {
                    None
                }
            }
            Expression::BigIntLiteral(lit) => lit.to_big_int(),
            Expression::BooleanLiteral(bool_literal) => {
                if bool_literal.value {
                    Some(BigInt::one())
                } else {
                    Some(BigInt::zero())
                }
            }
            Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
                UnaryOperator::LogicalNot => {
                    self.to_boolean().map(
                        |boolean| {
                            if boolean {
                                BigInt::one()
                            } else {
                                BigInt::zero()
                            }
                        },
                    )
                }
                UnaryOperator::UnaryNegation => {
                    unary_expr.argument.to_big_int().map(std::ops::Neg::neg)
                }
                UnaryOperator::BitwiseNot => {
                    unary_expr.argument.to_big_int().map(std::ops::Not::not)
                }
                UnaryOperator::UnaryPlus => unary_expr.argument.to_big_int(),
                _ => None,
            },
            Expression::StringLiteral(string_literal) => {
                string_literal.value.as_str().string_to_big_int()
            }
            Expression::TemplateLiteral(_) => {
                self.to_js_string().and_then(|value| value.as_ref().string_to_big_int())
            }
            _ => None,
        }
    }
}

impl<'a> ToBigInt<'a> for BigIntLiteral<'a> {
    fn to_big_int(&self) -> Option<BigInt> {
        let value = self.raw.as_str().trim_end_matches('n').string_to_big_int();
        debug_assert!(value.is_some(), "Failed to parse {}", self.raw);
        value
    }
}