diff --git a/boa/Cargo.toml b/boa/Cargo.toml index 5d321169355..63b2af73816 100644 --- a/boa/Cargo.toml +++ b/boa/Cargo.toml @@ -23,7 +23,7 @@ console = [] [dependencies] boa_unicode = { path = "../boa_unicode", version = "0.11.0" } gc = { version = "0.4.1", features = ["derive"] } -serde = { version = "1.0.126", features = ["derive"] } +serde = { version = "1.0.126", features = ["derive", "rc"] } serde_json = "1.0.64" rand = "0.8.4" num-traits = "0.2.14" diff --git a/boa/src/bigint.rs b/boa/src/bigint.rs new file mode 100644 index 00000000000..cbc7aac473f --- /dev/null +++ b/boa/src/bigint.rs @@ -0,0 +1,437 @@ +//! This module implements the JavaScript bigint primitive rust type. + +use crate::{ + builtins::Number, + gc::{empty_trace, Finalize, Trace}, + Context, Value, +}; + +use std::{ + convert::TryFrom, + fmt::{self, Display}, + ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, + rc::Rc, +}; + +use num_integer::Integer; +use num_traits::pow::Pow; +use num_traits::{FromPrimitive, One, ToPrimitive, Zero}; + +/// The raw bigint type. +pub type RawBigInt = num_bigint::BigInt; + +#[cfg(feature = "deser")] +use serde::{Deserialize, Serialize}; + +/// JavaScript bigint primitive rust type. +#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct JsBigInt { + inner: Rc, +} + +// Safety: BigInt does not contain any objects which needs to be traced, +// so this is safe. +unsafe impl Trace for JsBigInt { + empty_trace!(); +} + +impl JsBigInt { + /// Create a new [`JsBigInt`]. + #[inline] + pub fn new>(value: T) -> Self { + value.into() + } + + /// Create a [`JsBigInt`] with value `0`. + #[inline] + pub fn zero() -> Self { + Self { + inner: Rc::new(RawBigInt::zero()), + } + } + + /// Check if is zero. + #[inline] + pub fn is_zero(&self) -> bool { + self.inner.is_zero() + } + + /// Create a [`JsBigInt`] with value `1`. + #[inline] + pub fn one() -> Self { + Self { + inner: Rc::new(RawBigInt::one()), + } + } + + /// Check if is one. + #[inline] + pub fn is_one(&self) -> bool { + self.inner.is_one() + } + + /// Convert bigint to string with radix. + #[inline] + pub fn to_string_radix(&self, radix: u32) -> String { + self.inner.to_str_radix(radix) + } + + /// Converts the BigInt to a f64 type. + /// + /// Returns `f64::INFINITY` if the BigInt is too big. + #[inline] + pub fn to_f64(&self) -> f64 { + self.inner.to_f64().unwrap_or(f64::INFINITY) + } + + /// Converts a string to a BigInt with the specified radix. + #[inline] + pub fn from_string_radix(buf: &str, radix: u32) -> Option { + Some(Self { + inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?), + }) + } + + /// This function takes a string and conversts it to BigInt type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint + #[inline] + pub fn from_string(mut string: &str) -> Option { + string = string.trim(); + + if string.is_empty() { + return Some(JsBigInt::zero()); + } + + let mut radix = 10; + if string.starts_with("0b") || string.starts_with("0B") { + radix = 2; + string = &string[2..]; + } else if string.starts_with("0x") || string.starts_with("0X") { + radix = 16; + string = &string[2..]; + } else if string.starts_with("0o") || string.starts_with("0O") { + radix = 8; + string = &string[2..]; + } + + Self::from_string_radix(string, radix) + } + + /// Checks for `SameValueZero` equality. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal + #[inline] + pub fn same_value_zero(x: &Self, y: &Self) -> bool { + // Return BigInt::equal(x, y) + Self::equal(x, y) + } + + /// Checks for `SameValue` equality. + /// + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue + #[inline] + pub fn same_value(x: &Self, y: &Self) -> bool { + // Return BigInt::equal(x, y) + Self::equal(x, y) + } + + /// Checks for mathematical equality. + /// + /// The abstract operation BigInt::equal takes arguments x (a `BigInt`) and y (a `BigInt`). + /// It returns `true` if x and y have the same mathematical integer value and false otherwise. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero + #[inline] + pub fn equal(x: &Self, y: &Self) -> bool { + x == y + } + + #[inline] + pub fn pow(x: &Self, y: &Self, context: &mut Context) -> Result { + let y = if let Some(y) = y.inner.to_biguint() { + y + } else { + return Err(context.construct_range_error("BigInt negative exponent")); + }; + + Ok(Self::new(x.inner.as_ref().clone().pow(y))) + } + + #[inline] + pub fn shift_right(x: &Self, y: &Self, context: &mut Context) -> Result { + if let Some(n) = y.inner.to_i32() { + let inner = if n > 0 { + x.inner.as_ref().clone().shr(n as usize) + } else { + x.inner.as_ref().clone().shl(n.abs() as usize) + }; + + Ok(Self::new(inner)) + } else { + Err(context.construct_range_error("Maximum BigInt size exceeded")) + } + } + + #[inline] + pub fn shift_left(x: &Self, y: &Self, context: &mut Context) -> Result { + if let Some(n) = y.inner.to_i32() { + let inner = if n > 0 { + x.inner.as_ref().clone().shl(n as usize) + } else { + x.inner.as_ref().clone().shr(n.abs() as usize) + }; + + Ok(Self::new(inner)) + } else { + Err(context.construct_range_error("Maximum BigInt size exceeded")) + } + } + + /// Floored integer modulo. + /// + /// # Examples + /// ``` + /// # use num_integer::Integer; + /// assert_eq!((8).mod_floor(&3), 2); + /// assert_eq!((8).mod_floor(&-3), -1); + /// ``` + #[inline] + pub fn mod_floor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.mod_floor(&y.inner)) + } + + #[inline] + pub fn add(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().add(y.inner.as_ref())) + } + + #[inline] + pub fn sub(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref())) + } + + #[inline] + pub fn mul(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref())) + } + + #[inline] + pub fn div(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().div(y.inner.as_ref())) + } + + #[inline] + pub fn rem(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref())) + } + + #[inline] + pub fn bitand(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref())) + } + + #[inline] + pub fn bitor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref())) + } + + #[inline] + pub fn bitxor(x: &Self, y: &Self) -> Self { + Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref())) + } + + #[inline] + pub fn neg(x: &Self) -> Self { + Self::new(x.as_inner().neg()) + } + + #[inline] + pub(crate) fn as_inner(&self) -> &RawBigInt { + &self.inner + } +} + +impl Display for JsBigInt { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: RawBigInt) -> Self { + Self { + inner: Rc::new(value), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i8) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u8) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i16) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u16) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i32) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u32) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: i64) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: u64) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: isize) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +impl From for JsBigInt { + #[inline] + fn from(value: usize) -> Self { + Self { + inner: Rc::new(RawBigInt::from(value)), + } + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct TryFromF64Error; + +impl Display for TryFromF64Error { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert f64 value to a BigInt type") + } +} + +impl TryFrom for JsBigInt { + type Error = TryFromF64Error; + + #[inline] + fn try_from(n: f64) -> Result { + // If the truncated version of the number is not the + // same as the non-truncated version then the floating-point + // number conains a fractional part. + if !Number::equal(n.trunc(), n) { + return Err(TryFromF64Error); + } + match RawBigInt::from_f64(n) { + Some(bigint) => Ok(Self::new(bigint)), + None => Err(TryFromF64Error), + } + } +} + +impl PartialEq for JsBigInt { + #[inline] + fn eq(&self, other: &i32) -> bool { + self.inner.as_ref() == &RawBigInt::from(*other) + } +} + +impl PartialEq for i32 { + #[inline] + fn eq(&self, other: &JsBigInt) -> bool { + &RawBigInt::from(*self) == other.inner.as_ref() + } +} + +impl PartialEq for JsBigInt { + #[inline] + fn eq(&self, other: &f64) -> bool { + if other.fract() != 0.0 { + return false; + } + + self.inner.as_ref() == &RawBigInt::from(*other as i64) + } +} + +impl PartialEq for f64 { + #[inline] + fn eq(&self, other: &JsBigInt) -> bool { + if self.fract() != 0.0 { + return false; + } + + &RawBigInt::from(*self as i64) == other.inner.as_ref() + } +} diff --git a/boa/src/builtins/bigint/conversions.rs b/boa/src/builtins/bigint/conversions.rs deleted file mode 100644 index 2c8dd37d6b6..00000000000 --- a/boa/src/builtins/bigint/conversions.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::BigInt; - -use crate::{builtins::Number, Context, Value}; -use num_traits::cast::{FromPrimitive, ToPrimitive}; - -use std::convert::TryFrom; -use std::str::FromStr; - -impl BigInt { - /// This function takes a string and conversts it to BigInt type. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint - #[inline] - pub(crate) fn from_string(string: &str, context: &mut Context) -> Result { - if string.trim().is_empty() { - return Ok(BigInt::from(0)); - } - - let mut radix = 10; - let mut string = string; - if string.starts_with("0b") || string.starts_with("0B") { - radix = 2; - string = &string[2..]; - } - if string.starts_with("0x") || string.starts_with("0X") { - radix = 16; - string = &string[2..]; - } - if string.starts_with("0o") || string.starts_with("0O") { - radix = 8; - string = &string[2..]; - } - - BigInt::from_string_radix(string, radix).ok_or_else(|| { - context.construct_syntax_error(format!("cannot convert {} to a BigInt", string)) - }) - } - - /// Converts a string to a BigInt with the specified radix. - #[inline] - pub fn from_string_radix(buf: &str, radix: u32) -> Option { - num_bigint::BigInt::parse_bytes(buf.as_bytes(), radix).map(Self) - } - - /// Convert bigint to string with radix. - #[inline] - pub fn to_string_radix(&self, radix: u32) -> String { - self.0.to_str_radix(radix) - } - - /// Converts the BigInt to a f64 type. - /// - /// Returns `f64::INFINITY` if the BigInt is too big. - #[inline] - pub fn to_f64(&self) -> f64 { - self.0.to_f64().unwrap_or(f64::INFINITY) - } - - #[inline] - pub(crate) fn from_str(string: &str) -> Option { - match num_bigint::BigInt::from_str(string) { - Ok(bigint) => Some(BigInt(bigint)), - Err(_) => None, - } - } -} - -impl From for BigInt { - fn from(n: i64) -> BigInt { - BigInt(num_bigint::BigInt::from(n)) - } -} - -impl From for BigInt { - fn from(n: i32) -> BigInt { - BigInt(num_bigint::BigInt::from(n)) - } -} - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct TryFromF64Error; - -impl std::fmt::Display for TryFromF64Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Could not convert f64 value to a BigInt type") - } -} - -impl TryFrom for BigInt { - type Error = TryFromF64Error; - - fn try_from(n: f64) -> Result { - // If the truncated version of the number is not the - // same as the non-truncated version then the floating-point - // number conains a fractional part. - if !Number::equal(n.trunc(), n) { - return Err(TryFromF64Error); - } - match num_bigint::BigInt::from_f64(n) { - Some(bigint) => Ok(BigInt(bigint)), - None => Err(TryFromF64Error), - } - } -} diff --git a/boa/src/builtins/bigint/equality.rs b/boa/src/builtins/bigint/equality.rs deleted file mode 100644 index 9eee4935a12..00000000000 --- a/boa/src/builtins/bigint/equality.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::BigInt; - -impl BigInt { - /// Checks for `SameValueZero` equality. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal - #[inline] - pub(crate) fn same_value_zero(x: &Self, y: &Self) -> bool { - // Return BigInt::equal(x, y) - Self::equal(x, y) - } - - /// Checks for `SameValue` equality. - /// - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue - #[inline] - pub(crate) fn same_value(x: &Self, y: &Self) -> bool { - // Return BigInt::equal(x, y) - Self::equal(x, y) - } - - /// Checks for mathematical equality. - /// - /// The abstract operation BigInt::equal takes arguments x (a `BigInt`) and y (a `BigInt`). - /// It returns `true` if x and y have the same mathematical integer value and false otherwise. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero - #[inline] - pub(crate) fn equal(x: &Self, y: &Self) -> bool { - x == y - } -} - -impl PartialEq for BigInt { - fn eq(&self, other: &i32) -> bool { - self.0 == num_bigint::BigInt::from(*other) - } -} - -impl PartialEq for i32 { - fn eq(&self, other: &BigInt) -> bool { - num_bigint::BigInt::from(*self) == other.0 - } -} - -impl PartialEq for BigInt { - fn eq(&self, other: &f64) -> bool { - if other.fract() != 0.0 { - return false; - } - - self.0 == num_bigint::BigInt::from(*other as i64) - } -} - -impl PartialEq for f64 { - fn eq(&self, other: &BigInt) -> bool { - if self.fract() != 0.0 { - return false; - } - - num_bigint::BigInt::from(*self as i64) == other.0 - } -} diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index b9df3548678..ff6fc2f9af8 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -14,32 +14,17 @@ use crate::{ builtins::BuiltIn, - gc::{empty_trace, Finalize, Trace}, object::{ConstructorBuilder, ObjectData}, property::Attribute, symbol::WellKnownSymbols, - value::{RcBigInt, Value}, - BoaProfiler, Context, Result, + BoaProfiler, Context, JsBigInt, Result, Value, }; - -#[cfg(feature = "deser")] -use serde::{Deserialize, Serialize}; - -pub mod conversions; -pub mod equality; -pub mod operations; - -pub use conversions::*; -pub use equality::*; -pub use operations::*; - #[cfg(test)] mod tests; /// `BigInt` implementation. -#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] -#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] -pub struct BigInt(num_bigint::BigInt); +#[derive(Debug, Clone, Copy)] +pub struct BigInt; impl BuiltIn for BigInt { const NAME: &'static str = "BigInt"; @@ -94,7 +79,7 @@ impl BigInt { fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result { let data = match args.get(0) { Some(ref value) => value.to_bigint(context)?, - None => RcBigInt::from(Self::from(0)), + None => JsBigInt::zero(), }; Ok(Value::from(data)) } @@ -110,7 +95,7 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue #[inline] - fn this_bigint_value(value: &Value, context: &mut Context) -> Result { + fn this_bigint_value(value: &Value, context: &mut Context) -> Result { match value { // 1. If Type(value) is BigInt, return value. Value::BigInt(ref bigint) => return Ok(bigint.clone()), @@ -181,17 +166,12 @@ impl BigInt { let (modulo, bits) = Self::calculate_as_uint_n(args, context)?; if bits > 0 - && modulo - >= BigInt::from(2) - .pow(&BigInt::from(bits as i64 - 1)) - .expect("the exponent must be positive") + && modulo >= JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(bits as i64 - 1), context)? { - Ok(Value::from( - modulo - - BigInt::from(2) - .pow(&BigInt::from(bits as i64)) - .expect("the exponent must be positive"), - )) + Ok(Value::from(JsBigInt::sub( + &modulo, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(bits as i64), context)?, + ))) } else { Ok(Value::from(modulo)) } @@ -215,7 +195,7 @@ impl BigInt { /// This function expects the same arguments as `as_uint_n` and wraps the value of a `BigInt`. /// Additionally to the wrapped unsigned value it returns the converted `bits` argument, so it /// can be reused from the `as_int_n` method. - fn calculate_as_uint_n(args: &[Value], context: &mut Context) -> Result<(BigInt, u32)> { + fn calculate_as_uint_n(args: &[Value], context: &mut Context) -> Result<(JsBigInt, u32)> { use std::convert::TryFrom; let undefined_value = Value::undefined(); @@ -229,29 +209,11 @@ impl BigInt { let bigint = bigint_arg.to_bigint(context)?; Ok(( - bigint.as_inner().clone().mod_floor( - &BigInt::from(2) - .pow(&BigInt::from(bits as i64)) - .expect("the exponent must be positive"), + JsBigInt::mod_floor( + &bigint, + &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(bits as i64), context)?, ), bits, )) } - - /// helper function for checking if the BigInt represents 0 - /// - /// creating BigInts requires an allocation and for a few operations we need to know if the - /// inner value is 0, this solves that situation - pub(crate) fn is_zero(&self) -> bool { - use num_traits::Zero; - self.0.is_zero() - } -} - -impl Finalize for BigInt {} -unsafe impl Trace for BigInt { - // BigInt type implements an empty trace becasue the inner structure - // `num_bigint::BigInt` does not implement `Trace` trait. - // If it did this would be unsound. - empty_trace!(); } diff --git a/boa/src/builtins/bigint/operations.rs b/boa/src/builtins/bigint/operations.rs deleted file mode 100644 index 7edbc898c1d..00000000000 --- a/boa/src/builtins/bigint/operations.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! This module implements the `BigInt` operations. - -use num_traits::cast::ToPrimitive; -use num_traits::pow::Pow; - -use super::BigInt; - -impl BigInt { - #[inline] - pub fn pow(self, other: &Self) -> Result { - Ok(Self(self.0.pow( - other.0.to_biguint().ok_or("BigInt negative exponent")?, - ))) - } - - #[inline] - pub fn shift_right(mut self, other: Self) -> Result { - use std::ops::ShlAssign; - use std::ops::ShrAssign; - - if let Some(n) = other.0.to_i32() { - if n > 0 { - self.0.shr_assign(n as usize) - } else { - self.0.shl_assign(n.abs() as usize) - } - - Ok(self) - } else { - Err("Maximum BigInt size exceeded".into()) - } - } - - #[inline] - pub fn shift_left(mut self, other: Self) -> Result { - use std::ops::ShlAssign; - use std::ops::ShrAssign; - - if let Some(n) = other.0.to_i32() { - if n > 0 { - self.0.shl_assign(n as usize) - } else { - self.0.shr_assign(n.abs() as usize) - } - - Ok(self) - } else { - Err("Maximum BigInt size exceeded".into()) - } - } - - /// Floored integer modulo. - /// - /// # Examples - /// ``` - /// # use num_integer::Integer; - /// assert_eq!((8).mod_floor(&3), 2); - /// assert_eq!((8).mod_floor(&-3), -1); - /// ``` - #[inline] - pub fn mod_floor(self, other: &Self) -> Self { - use num_integer::Integer; - Self(self.0.mod_floor(&other.0)) - } -} - -macro_rules! impl_bigint_operator { - ($op:ident, $op_method:ident, $assign_op:ident, $assign_op_method:ident) => { - impl std::ops::$op for BigInt { - type Output = Self; - - fn $op_method(mut self, other: Self) -> Self { - std::ops::$assign_op::$assign_op_method(&mut self.0, other.0); - self - } - } - }; -} - -impl_bigint_operator!(Add, add, AddAssign, add_assign); -impl_bigint_operator!(Sub, sub, SubAssign, sub_assign); -impl_bigint_operator!(Mul, mul, MulAssign, mul_assign); -impl_bigint_operator!(Div, div, DivAssign, div_assign); -impl_bigint_operator!(Rem, rem, RemAssign, rem_assign); -impl_bigint_operator!(BitAnd, bitand, BitAndAssign, bitand_assign); -impl_bigint_operator!(BitOr, bitor, BitOrAssign, bitor_assign); -impl_bigint_operator!(BitXor, bitxor, BitXorAssign, bitxor_assign); - -impl std::ops::Neg for BigInt { - type Output = Self; - - fn neg(self) -> Self::Output { - Self(-self.0) - } -} - -impl std::fmt::Debug for BigInt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::Display for BigInt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} diff --git a/boa/src/bytecompiler.rs b/boa/src/bytecompiler.rs index 179d663c7b7..8d063ecce4e 100644 --- a/boa/src/bytecompiler.rs +++ b/boa/src/bytecompiler.rs @@ -4,16 +4,15 @@ use crate::{ op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, Const, Node, }, - value::RcBigInt, vm::{CodeBlock, Opcode}, - JsString, Value, + JsBigInt, JsString, Value, }; use std::collections::HashMap; #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum Literal { String(JsString), - BigInt(RcBigInt), + BigInt(JsBigInt), } #[must_use] @@ -352,7 +351,7 @@ impl ByteCompiler { Const::String(v) => self.emit_push_literal(Literal::String(v.as_ref().into())), Const::Int(v) => self.emit_push_integer(*v), Const::Num(v) => self.emit_push_rational(*v), - Const::BigInt(v) => self.emit_push_literal(Literal::BigInt(v.clone().into())), + Const::BigInt(v) => self.emit_push_literal(Literal::BigInt(v.clone())), Const::Bool(true) => self.emit(Opcode::PushTrue, &[]), Const::Bool(false) => self.emit(Opcode::PushFalse, &[]), Const::Null => self.emit(Opcode::PushNull, &[]), diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 1afa7ddc603..689f569f418 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -42,6 +42,7 @@ This is an experimental Javascript lexer, parser and compiler written in Rust. C missing_doc_code_examples )] +pub mod bigint; // builtins module has a lot of built-in functions that need unnecessary_wraps #[allow(clippy::unnecessary_wraps)] pub mod builtins; @@ -72,7 +73,9 @@ pub(crate) use crate::{exec::Executable, profiler::BoaProfiler}; // Export things to root level #[doc(inline)] -pub use crate::{context::Context, string::JsString, symbol::JsSymbol, value::Value}; +pub use crate::{ + bigint::JsBigInt, context::Context, string::JsString, symbol::JsSymbol, value::Value, +}; use crate::syntax::{ ast::node::StatementList, diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 88efd42b4ec..8c26016e275 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -10,13 +10,12 @@ use crate::{ set::ordered_set::OrderedSet, set::set_iterator::SetIterator, string::string_iterator::StringIterator, - BigInt, Date, RegExp, + Date, RegExp, }, context::StandardConstructor, gc::{Finalize, Trace}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, - value::{RcBigInt, Value}, - BoaProfiler, Context, JsString, JsSymbol, + BoaProfiler, Context, JsBigInt, JsString, JsSymbol, Value, }; use rustc_hash::FxHashMap; use std::{ @@ -87,7 +86,7 @@ pub enum ObjectData { MapIterator(MapIterator), RegExp(Box), RegExpStringIterator(RegExpStringIterator), - BigInt(RcBigInt), + BigInt(JsBigInt), Boolean(bool), ForInIterator(ForInIterator), Function(Function), @@ -230,7 +229,7 @@ impl Object { /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument. #[inline] - pub fn bigint(value: RcBigInt) -> Self { + pub fn bigint(value: JsBigInt) -> Self { Self { data: ObjectData::BigInt(value), indexed_properties: FxHashMap::default(), @@ -497,7 +496,7 @@ impl Object { } #[inline] - pub fn as_bigint(&self) -> Option<&BigInt> { + pub fn as_bigint(&self) -> Option<&JsBigInt> { match self.data { ObjectData::BigInt(ref bigint) => Some(bigint), _ => None, diff --git a/boa/src/string.rs b/boa/src/string.rs index 60cfc50dc6c..b52bc0f0ad8 100644 --- a/boa/src/string.rs +++ b/boa/src/string.rs @@ -307,7 +307,7 @@ impl Hash for JsString { impl PartialOrd for JsString { #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.as_str().partial_cmp(other) + self.as_str().partial_cmp(other.as_str()) } } diff --git a/boa/src/syntax/ast/constant.rs b/boa/src/syntax/ast/constant.rs index 2afb713b9b3..6fb6a9939b4 100644 --- a/boa/src/syntax/ast/constant.rs +++ b/boa/src/syntax/ast/constant.rs @@ -8,8 +8,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals use crate::{ - builtins::bigint::BigInt, gc::{Finalize, Trace}, + JsBigInt, }; use std::fmt::{Display, Formatter, Result}; @@ -76,7 +76,7 @@ pub enum Const { /// /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals - BigInt(BigInt), + BigInt(JsBigInt), /// The Boolean type has two literal values: `true` and `false`. /// @@ -151,8 +151,8 @@ impl From for Const { } } -impl From for Const { - fn from(i: BigInt) -> Self { +impl From for Const { + fn from(i: JsBigInt) -> Self { Self::BigInt(i) } } diff --git a/boa/src/syntax/lexer/number.rs b/boa/src/syntax/lexer/number.rs index e58fccfe202..38da06db679 100644 --- a/boa/src/syntax/lexer/number.rs +++ b/boa/src/syntax/lexer/number.rs @@ -2,12 +2,12 @@ use super::{Cursor, Error, TokenKind, Tokenizer}; use crate::{ - builtins::BigInt, profiler::BoaProfiler, syntax::{ ast::{Position, Span}, lexer::{token::Numeric, Token}, }, + JsBigInt, }; use std::io::Read; use std::str; @@ -377,7 +377,7 @@ impl Tokenizer for NumberLiteral { let num = match kind { NumericKind::BigInt(base) => { Numeric::BigInt( - BigInt::from_string_radix(num_str, base).expect("Could not convert to BigInt") + JsBigInt::from_string_radix(num_str, base).expect("Could not convert to BigInt") ) } NumericKind::Rational /* base: 10 */ => { diff --git a/boa/src/syntax/lexer/token.rs b/boa/src/syntax/lexer/token.rs index e2042c2f33e..ecbb2c8c0e2 100644 --- a/boa/src/syntax/lexer/token.rs +++ b/boa/src/syntax/lexer/token.rs @@ -8,9 +8,9 @@ use super::regex::RegExpFlags; use crate::{ - builtins::BigInt, syntax::ast::{Keyword, Punctuator, Span}, syntax::lexer::template::TemplateString, + JsBigInt, }; use std::fmt::{self, Debug, Display, Formatter}; @@ -69,7 +69,7 @@ pub enum Numeric { Integer(i32), // A BigInt - BigInt(BigInt), + BigInt(JsBigInt), } impl From for Numeric { @@ -86,9 +86,9 @@ impl From for Numeric { } } -impl From for Numeric { +impl From for Numeric { #[inline] - fn from(n: BigInt) -> Self { + fn from(n: JsBigInt) -> Self { Self::BigInt(n) } } diff --git a/boa/src/value/conversions.rs b/boa/src/value/conversions.rs index 13107ddfdf8..137c33eb773 100644 --- a/boa/src/value/conversions.rs +++ b/boa/src/value/conversions.rs @@ -90,14 +90,8 @@ impl From for Value { } } -impl From for Value { - fn from(value: BigInt) -> Self { - Value::bigint(value) - } -} - -impl From for Value { - fn from(value: RcBigInt) -> Self { +impl From for Value { + fn from(value: JsBigInt) -> Self { Value::BigInt(value) } } diff --git a/boa/src/value/equality.rs b/boa/src/value/equality.rs index 9165e23ca35..f246f2ff47f 100644 --- a/boa/src/value/equality.rs +++ b/boa/src/value/equality.rs @@ -15,7 +15,7 @@ impl Value { match (self, other) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::equal(x, y). - (Self::BigInt(x), Self::BigInt(y)) => BigInt::equal(x, y), + (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::equal(x, y), (Self::Rational(x), Self::Rational(y)) => Number::equal(*x, *y), (Self::Rational(x), Self::Integer(y)) => Number::equal(*x, f64::from(*y)), (Self::Integer(x), Self::Rational(y)) => Number::equal(f64::from(*x), *y), @@ -68,14 +68,14 @@ impl Value { // a. Let n be ! StringToBigInt(y). // b. If n is NaN, return false. // c. Return the result of the comparison x == n. - (Self::BigInt(ref a), Self::String(ref b)) => match string_to_bigint(b) { - Some(ref b) => a.as_inner() == b, + (Self::BigInt(ref a), Self::String(ref b)) => match JsBigInt::from_string(b) { + Some(ref b) => a == b, None => false, }, // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. - (Self::String(ref a), Self::BigInt(ref b)) => match string_to_bigint(a) { - Some(ref a) => a == b.as_inner(), + (Self::String(ref a), Self::BigInt(ref b)) => match JsBigInt::from_string(a) { + Some(ref a) => a == b, None => false, }, @@ -102,10 +102,10 @@ impl Value { // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then // a. If x or y are any of NaN, +∞, or -∞, return false. // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. - (Self::BigInt(ref a), Self::Rational(ref b)) => a.as_inner() == b, - (Self::Rational(ref a), Self::BigInt(ref b)) => a == b.as_inner(), - (Self::BigInt(ref a), Self::Integer(ref b)) => a.as_inner() == b, - (Self::Integer(ref a), Self::BigInt(ref b)) => a == b.as_inner(), + (Self::BigInt(ref a), Self::Rational(ref b)) => a == b, + (Self::Rational(ref a), Self::BigInt(ref b)) => a == b, + (Self::BigInt(ref a), Self::Integer(ref b)) => a == b, + (Self::Integer(ref a), Self::BigInt(ref b)) => a == b, // 13. Return false. _ => false, @@ -128,7 +128,7 @@ impl Value { match (x, y) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValue(x, y). - (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value(x, y), + (Value::BigInt(x), Value::BigInt(y)) => JsBigInt::same_value(x, y), (Value::Rational(x), Value::Rational(y)) => Number::same_value(*x, *y), (Value::Rational(x), Value::Integer(y)) => Number::same_value(*x, f64::from(*y)), (Value::Integer(x), Value::Rational(y)) => Number::same_value(f64::from(*x), *y), @@ -156,7 +156,7 @@ impl Value { match (x, y) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValueZero(x, y). - (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value_zero(x, y), + (Value::BigInt(x), Value::BigInt(y)) => JsBigInt::same_value_zero(x, y), (Value::Rational(x), Value::Rational(y)) => Number::same_value_zero(*x, *y), (Value::Rational(x), Value::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)), @@ -180,19 +180,3 @@ impl Value { } } } - -/// This function takes a string and conversts it to BigInt type. -/// -/// If the result is `NaN` than `None` is returned. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-stringtobigint -pub fn string_to_bigint(string: &str) -> Option { - if string.is_empty() { - return Some(BigInt::from(0)); - } - - BigInt::from_str(string) -} diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 8bdfc4dfa66..95ba30773eb 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -9,12 +9,12 @@ use crate::{ builtins::{ number::{f64_to_int32, f64_to_uint32}, string::is_trimmable_whitespace, - BigInt, Number, + Number, }, object::{GcObject, Object, ObjectData}, property::{Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, symbol::{JsSymbol, WellKnownSymbols}, - BoaProfiler, Context, JsString, Result, + BoaProfiler, Context, JsBigInt, JsString, Result, }; use gc::{Finalize, Trace}; use serde_json::{Number as JSONNumber, Value as JSONValue}; @@ -30,7 +30,6 @@ pub(crate) mod display; mod equality; mod hash; mod operations; -mod rcbigint; mod r#type; pub use conversions::*; @@ -39,7 +38,6 @@ pub use equality::*; pub use hash::*; pub use operations::*; pub use r#type::Type; -pub use rcbigint::RcBigInt; /// A Javascript value #[derive(Trace, Finalize, Debug, Clone)] @@ -57,7 +55,7 @@ pub enum Value { /// `Number` - A 32-bit integer, such as `42`. Integer(i32), /// `BigInt` - holds any arbitrary large signed integer. - BigInt(RcBigInt), + BigInt(JsBigInt), /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. Object(GcObject), /// `Symbol` - A Symbol Primitive type. @@ -143,7 +141,7 @@ impl Value { #[inline] pub fn bigint(value: B) -> Self where - B: Into, + B: Into, { Self::BigInt(value.into()) } @@ -395,7 +393,7 @@ impl Value { /// Returns an optional reference to a `BigInt` if the value is a BigInt primitive. #[inline] - pub fn as_bigint(&self) -> Option<&BigInt> { + pub fn as_bigint(&self) -> Option<&JsBigInt> { match self { Self::BigInt(bigint) => Some(bigint), _ => None, @@ -415,7 +413,7 @@ impl Value { Self::String(ref s) if !s.is_empty() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Integer(n) if n != 0 => true, - Self::BigInt(ref n) if *n.as_inner() != 0 => true, + Self::BigInt(ref n) if !n.is_zero() => true, Self::Boolean(v) => v, _ => false, } @@ -592,19 +590,28 @@ impl Value { /// Converts the value to a `BigInt`. /// /// This function is equivelent to `BigInt(value)` in JavaScript. - pub fn to_bigint(&self, context: &mut Context) -> Result { + pub fn to_bigint(&self, context: &mut Context) -> Result { match self { Value::Null => Err(context.construct_type_error("cannot convert null to a BigInt")), Value::Undefined => { Err(context.construct_type_error("cannot convert undefined to a BigInt")) } - Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, context)?)), - Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))), - Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))), - Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))), + Value::String(ref string) => { + if let Some(value) = JsBigInt::from_string(string) { + Ok(value) + } else { + Err(context.construct_syntax_error(format!( + "cannot convert string '{}' to bigint primitive", + string + ))) + } + } + Value::Boolean(true) => Ok(JsBigInt::one()), + Value::Boolean(false) => Ok(JsBigInt::zero()), + Value::Integer(num) => Ok(JsBigInt::new(*num)), Value::Rational(num) => { - if let Ok(bigint) = BigInt::try_from(*num) { - return Ok(RcBigInt::from(bigint)); + if let Ok(bigint) = JsBigInt::try_from(*num) { + return Ok(bigint); } Err(context.construct_type_error(format!( "The number {} cannot be converted to a BigInt because it is not an integer", @@ -978,7 +985,7 @@ pub enum Numeric { /// Double precision floating point number. Number(f64), /// BigInt an integer of arbitrary size. - BigInt(RcBigInt), + BigInt(JsBigInt), } impl From for Numeric { @@ -1030,16 +1037,9 @@ impl From for Numeric { } } -impl From for Numeric { - #[inline] - fn from(value: BigInt) -> Self { - Self::BigInt(value.into()) - } -} - -impl From for Numeric { +impl From for Numeric { #[inline] - fn from(value: RcBigInt) -> Self { + fn from(value: JsBigInt) -> Self { Self::BigInt(value) } } diff --git a/boa/src/value/operations.rs b/boa/src/value/operations.rs index cbd30172e2f..7439d363e46 100644 --- a/boa/src/value/operations.rs +++ b/boa/src/value/operations.rs @@ -110,7 +110,7 @@ impl Value { (Self::Rational(x), Self::Integer(y)) => Self::rational(x / f64::from(*y)), (Self::BigInt(ref a), Self::BigInt(ref b)) => { - if b.as_inner().is_zero() { + if b.is_zero() { return context.throw_range_error("BigInt division by zero"); } Self::bigint(a.as_inner().clone() / b.as_inner().clone()) @@ -120,7 +120,7 @@ impl Value { (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a / b), (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { - if b.as_inner().is_zero() { + if b.is_zero() { return context.throw_range_error("BigInt division by zero"); } Self::bigint(a.as_inner().clone() / b.as_inner().clone()) @@ -150,7 +150,7 @@ impl Value { (Self::Rational(x), Self::Integer(y)) => Self::rational(x % f64::from(*y)), (Self::BigInt(ref a), Self::BigInt(ref b)) => { - if b.as_inner().is_zero() { + if b.is_zero() { return context.throw_range_error("BigInt division by zero"); } Self::bigint(a.as_inner().clone() % b.as_inner().clone()) @@ -180,22 +180,16 @@ impl Value { (Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x).powf(*y)), (Self::Rational(x), Self::Integer(y)) => Self::rational(x.powi(*y)), - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint( - a.as_inner() - .clone() - .pow(b) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(JsBigInt::pow(a, b, context)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)), - (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::bigint( - a.as_inner() - .clone() - .pow(b) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { + Self::bigint(JsBigInt::pow(a, b, context)?) + } (_, _) => { return context.throw_type_error( "cannot mix BigInt and other types, use explicit conversions", @@ -316,24 +310,18 @@ impl Value { Self::integer(f64_to_int32(*x).wrapping_shl(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint( - a.as_inner() - .clone() - .shift_left(b.as_inner().clone()) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(JsBigInt::shift_left(a, b, context)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { (Numeric::Number(x), Numeric::Number(y)) => { Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y))) } - (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint( - x.as_inner() - .clone() - .shift_left(y.as_inner().clone()) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { + Self::bigint(JsBigInt::shift_left(x, y, context)?) + } (_, _) => { return context.throw_type_error( "cannot mix BigInt and other types, use explicit conversions", @@ -358,24 +346,18 @@ impl Value { Self::integer(f64_to_int32(*x).wrapping_shr(*y as u32)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint( - a.as_inner() - .clone() - .shift_right(b.as_inner().clone()) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(JsBigInt::shift_right(a, b, context)?) + } // Slow path: (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) { (Numeric::Number(x), Numeric::Number(y)) => { Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y))) } - (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint( - x.as_inner() - .clone() - .shift_right(y.as_inner().clone()) - .map_err(|msg| context.construct_range_error(msg))?, - ), + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { + Self::bigint(JsBigInt::shift_right(x, y, context)?) + } (_, _) => { return context.throw_type_error( "cannot mix BigInt and other types, use explicit conversions", @@ -505,15 +487,15 @@ impl Value { unreachable!() } (Self::BigInt(ref x), Self::String(ref y)) => { - if let Some(y) = string_to_bigint(&y) { - (*x.as_inner() < y).into() + if let Some(y) = JsBigInt::from_string(&y) { + (*x < y).into() } else { AbstractRelation::Undefined } } (Self::String(ref x), Self::BigInt(ref y)) => { - if let Some(x) = string_to_bigint(&x) { - (x < *y.as_inner()).into() + if let Some(x) = JsBigInt::from_string(&x) { + (x < *y).into() } else { AbstractRelation::Undefined } @@ -533,7 +515,7 @@ impl Value { } else { y.ceil() }; - (*x.as_inner() < BigInt::try_from(n).unwrap()).into() + (*x < JsBigInt::try_from(n).unwrap()).into() } (Numeric::Number(x), Numeric::BigInt(ref y)) => { if x.is_nan() { @@ -547,7 +529,7 @@ impl Value { } else { x.ceil() }; - (BigInt::try_from(n).unwrap() < *y.as_inner()).into() + (JsBigInt::try_from(n).unwrap() < *y).into() } }, } diff --git a/boa/src/value/rcbigint.rs b/boa/src/value/rcbigint.rs deleted file mode 100644 index 94f9465165c..00000000000 --- a/boa/src/value/rcbigint.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{ - builtins::BigInt, - gc::{empty_trace, Finalize, Trace}, -}; - -use std::{ - fmt::{self, Display}, - ops::Deref, - rc::Rc, -}; - -#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct RcBigInt(Rc); - -unsafe impl Trace for RcBigInt { - empty_trace!(); -} - -impl RcBigInt { - pub(crate) fn as_inner(&self) -> &BigInt { - &self.0 - } -} - -impl Display for RcBigInt { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl Deref for RcBigInt { - type Target = BigInt; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for RcBigInt { - #[inline] - fn from(bigint: BigInt) -> Self { - Self(Rc::from(bigint)) - } -}