oxc_ecmascript/
to_big_int.rs

1use num_bigint::BigInt;
2use num_traits::{Num, One, Zero};
3
4use oxc_ast::ast::{BigIntLiteral, Expression};
5use oxc_syntax::operator::UnaryOperator;
6
7use crate::{GlobalContext, StringToBigInt, ToBoolean, ToJsString};
8
9/// `ToBigInt`
10///
11/// <https://tc39.es/ecma262/#sec-tobigint>
12pub trait ToBigInt<'a> {
13    fn to_big_int(&self, ctx: &impl GlobalContext<'a>) -> Option<BigInt>;
14}
15
16impl<'a> ToBigInt<'a> for Expression<'a> {
17    #[expect(clippy::cast_possible_truncation)]
18    fn to_big_int(&self, ctx: &impl GlobalContext<'a>) -> Option<BigInt> {
19        match self {
20            Expression::NumericLiteral(number_literal) => {
21                let value = number_literal.value;
22                if value.abs() < 2_f64.powi(53) && value.fract() == 0.0 {
23                    Some(BigInt::from(value as i64))
24                } else {
25                    None
26                }
27            }
28            Expression::BigIntLiteral(lit) => lit.to_big_int(ctx),
29            Expression::BooleanLiteral(bool_literal) => {
30                if bool_literal.value {
31                    Some(BigInt::one())
32                } else {
33                    Some(BigInt::zero())
34                }
35            }
36            Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
37                UnaryOperator::LogicalNot => self
38                    .to_boolean(ctx)
39                    .map(|boolean| if boolean { BigInt::one() } else { BigInt::zero() }),
40                UnaryOperator::UnaryNegation => {
41                    unary_expr.argument.to_big_int(ctx).map(std::ops::Neg::neg)
42                }
43                UnaryOperator::BitwiseNot => {
44                    unary_expr.argument.to_big_int(ctx).map(std::ops::Not::not)
45                }
46                UnaryOperator::UnaryPlus => unary_expr.argument.to_big_int(ctx),
47                _ => None,
48            },
49            Expression::StringLiteral(string_literal) => {
50                string_literal.value.as_str().string_to_big_int()
51            }
52            Expression::TemplateLiteral(_) => {
53                self.to_js_string(ctx).and_then(|value| value.as_ref().string_to_big_int())
54            }
55            _ => None,
56        }
57    }
58}
59
60impl<'a> ToBigInt<'a> for BigIntLiteral<'a> {
61    fn to_big_int(&self, _ctx: &impl GlobalContext<'a>) -> Option<BigInt> {
62        // No need to use `StringToBigInt::string_to_big_int`, because `value` is always base 10
63        let value = BigInt::from_str_radix(&self.value, 10).ok();
64        debug_assert!(value.is_some(), "Failed to parse {}n", self.value);
65        value
66    }
67}