mirror of https://github.com/boa-dev/boa.git
Browse Source
- Merged Ast `BigInt` to Builtin `BigInt`. - Split `BigInt` logic to separate files. - Added `builtins/bigint/operations.rs` for `BigInt` operations. - Added `builtins/bigint/conversions.rs` for `BigInt` conversions. - Added` builtins/bigint/equality.rs` for `BigInt` equality checking. - Added tests.pull/460/head
HalidOdat
5 years ago
committed by
GitHub
19 changed files with 736 additions and 548 deletions
@ -0,0 +1,96 @@ |
|||||||
|
use super::BigInt; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
builtins::{Number, Value}, |
||||||
|
exec::Interpreter, |
||||||
|
}; |
||||||
|
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, _ctx: &mut Interpreter) -> Result<Self, Value> { |
||||||
|
if string.is_empty() { |
||||||
|
return Ok(BigInt::from(0)); |
||||||
|
} |
||||||
|
|
||||||
|
match num_bigint::BigInt::from_str(string) { |
||||||
|
Ok(bigint) => Ok(Self(bigint)), |
||||||
|
_ => panic!("SyntaxError: 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<Self> { |
||||||
|
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 `std::f64::INFINITY` if the BigInt is too big.
|
||||||
|
#[inline] |
||||||
|
pub fn to_f64(&self) -> f64 { |
||||||
|
self.0.to_f64().unwrap_or(std::f64::INFINITY) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
pub(crate) fn from_str(string: &str) -> Option<Self> { |
||||||
|
match num_bigint::BigInt::from_str(string) { |
||||||
|
Ok(bigint) => Some(BigInt(bigint)), |
||||||
|
Err(_) => None, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<i64> for BigInt { |
||||||
|
fn from(n: i64) -> BigInt { |
||||||
|
BigInt(num_bigint::BigInt::from(n)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<i32> 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<f64> for BigInt { |
||||||
|
type Error = TryFromF64Error; |
||||||
|
|
||||||
|
fn try_from(n: f64) -> Result<Self, Self::Error> { |
||||||
|
// 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), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
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<i32> for BigInt { |
||||||
|
fn eq(&self, other: &i32) -> bool { |
||||||
|
self.0 == num_bigint::BigInt::from(*other) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl PartialEq<BigInt> for i32 { |
||||||
|
fn eq(&self, other: &BigInt) -> bool { |
||||||
|
num_bigint::BigInt::from(*self) == other.0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl PartialEq<f64> 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<BigInt> for f64 { |
||||||
|
fn eq(&self, other: &BigInt) -> bool { |
||||||
|
if self.fract() != 0.0 { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
num_bigint::BigInt::from(*self as i64) == other.0 |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
//! 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) -> Self { |
||||||
|
Self( |
||||||
|
self.0.pow( |
||||||
|
other |
||||||
|
.0 |
||||||
|
.to_biguint() |
||||||
|
.expect("RangeError: \"BigInt negative exponent\""), |
||||||
|
), |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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::Shr for BigInt { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn shr(mut self, other: Self) -> Self::Output { |
||||||
|
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) |
||||||
|
} |
||||||
|
|
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
panic!("RangeError: Maximum BigInt size exceeded"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl std::ops::Shl for BigInt { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn shl(mut self, other: Self) -> Self::Output { |
||||||
|
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) |
||||||
|
} |
||||||
|
|
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
panic!("RangeError: Maximum BigInt size exceeded"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,215 @@ |
|||||||
|
use super::*; |
||||||
|
use crate::{builtins::Number, Interpreter}; |
||||||
|
|
||||||
|
use std::borrow::Borrow; |
||||||
|
|
||||||
|
impl Value { |
||||||
|
/// Strict equality comparison.
|
||||||
|
///
|
||||||
|
/// This method is executed when doing strict equality comparisons with the `===` operator.
|
||||||
|
/// For more information, check <https://tc39.es/ecma262/#sec-strict-equality-comparison>.
|
||||||
|
pub fn strict_equals(&self, other: &Self) -> bool { |
||||||
|
// 1. If Type(x) is different from Type(y), return false.
|
||||||
|
if self.get_type() != other.get_type() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
match (self.data(), other.data()) { |
||||||
|
// 2. If Type(x) is Number or BigInt, then
|
||||||
|
// a. Return ! Type(x)::equal(x, y).
|
||||||
|
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::equal(x, y), |
||||||
|
(ValueData::Rational(x), ValueData::Rational(y)) => Number::equal(*x, *y), |
||||||
|
(ValueData::Rational(x), ValueData::Integer(y)) => Number::equal(*x, f64::from(*y)), |
||||||
|
(ValueData::Integer(x), ValueData::Rational(y)) => Number::equal(f64::from(*x), *y), |
||||||
|
(ValueData::Integer(x), ValueData::Integer(y)) => x == y, |
||||||
|
|
||||||
|
//Null has to be handled specially because "typeof null" returns object and if we managed
|
||||||
|
//this without a special case we would compare self and other as if they were actually
|
||||||
|
//objects which unfortunately fails
|
||||||
|
//Specification Link: https://tc39.es/ecma262/#sec-typeof-operator
|
||||||
|
(ValueData::Null, ValueData::Null) => true, |
||||||
|
|
||||||
|
// 3. Return ! SameValueNonNumeric(x, y).
|
||||||
|
(_, _) => same_value_non_numeric(self, other), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Abstract equality comparison.
|
||||||
|
///
|
||||||
|
/// This method is executed when doing abstract equality comparisons with the `==` operator.
|
||||||
|
/// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
|
||||||
|
#[allow(clippy::float_cmp)] |
||||||
|
pub fn equals(&mut self, other: &mut Self, interpreter: &mut Interpreter) -> bool { |
||||||
|
// 1. If Type(x) is the same as Type(y), then
|
||||||
|
// a. Return the result of performing Strict Equality Comparison x === y.
|
||||||
|
if self.get_type() == other.get_type() { |
||||||
|
return self.strict_equals(other); |
||||||
|
} |
||||||
|
|
||||||
|
match (self.data(), other.data()) { |
||||||
|
// 2. If x is null and y is undefined, return true.
|
||||||
|
// 3. If x is undefined and y is null, return true.
|
||||||
|
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, |
||||||
|
|
||||||
|
// 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
|
||||||
|
// 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
|
||||||
|
//
|
||||||
|
// https://github.com/rust-lang/rust/issues/54883
|
||||||
|
(ValueData::Integer(_), ValueData::String(_)) |
||||||
|
| (ValueData::Rational(_), ValueData::String(_)) |
||||||
|
| (ValueData::String(_), ValueData::Integer(_)) |
||||||
|
| (ValueData::String(_), ValueData::Rational(_)) |
||||||
|
| (ValueData::Rational(_), ValueData::Boolean(_)) |
||||||
|
| (ValueData::Integer(_), ValueData::Boolean(_)) => { |
||||||
|
let a: &Value = self.borrow(); |
||||||
|
let b: &Value = other.borrow(); |
||||||
|
Number::equal(f64::from(a), f64::from(b)) |
||||||
|
} |
||||||
|
|
||||||
|
// 6. If Type(x) is BigInt and Type(y) is String, then
|
||||||
|
// a. Let n be ! StringToBigInt(y).
|
||||||
|
// b. If n is NaN, return false.
|
||||||
|
// c. Return the result of the comparison x == n.
|
||||||
|
(ValueData::BigInt(ref a), ValueData::String(ref b)) => match string_to_bigint(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.
|
||||||
|
(ValueData::String(ref a), ValueData::BigInt(ref b)) => match string_to_bigint(a) { |
||||||
|
Some(ref a) => a == b, |
||||||
|
None => false, |
||||||
|
}, |
||||||
|
|
||||||
|
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
|
||||||
|
(ValueData::Boolean(_), _) => { |
||||||
|
other.equals(&mut Value::from(self.to_integer()), interpreter) |
||||||
|
} |
||||||
|
|
||||||
|
// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
|
||||||
|
(_, ValueData::Boolean(_)) => { |
||||||
|
self.equals(&mut Value::from(other.to_integer()), interpreter) |
||||||
|
} |
||||||
|
|
||||||
|
// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result
|
||||||
|
// of the comparison x == ? ToPrimitive(y).
|
||||||
|
(ValueData::Object(_), _) => { |
||||||
|
let mut primitive = interpreter.to_primitive(self, None); |
||||||
|
primitive.equals(other, interpreter) |
||||||
|
} |
||||||
|
|
||||||
|
// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result
|
||||||
|
// of the comparison ? ToPrimitive(x) == y.
|
||||||
|
(_, ValueData::Object(_)) => { |
||||||
|
let mut primitive = interpreter.to_primitive(other, None); |
||||||
|
primitive.equals(self, interpreter) |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
(ValueData::BigInt(ref a), ValueData::Rational(ref b)) => a == b, |
||||||
|
(ValueData::Rational(ref a), ValueData::BigInt(ref b)) => a == b, |
||||||
|
(ValueData::BigInt(ref a), ValueData::Integer(ref b)) => a == b, |
||||||
|
(ValueData::Integer(ref a), ValueData::BigInt(ref b)) => a == b, |
||||||
|
|
||||||
|
// 13. Return false.
|
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// 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<BigInt> { |
||||||
|
if string.is_empty() { |
||||||
|
return Some(BigInt::from(0)); |
||||||
|
} |
||||||
|
|
||||||
|
BigInt::from_str(string) |
||||||
|
} |
||||||
|
|
||||||
|
/// The internal comparison abstract operation SameValue(x, y),
|
||||||
|
/// where x and y are ECMAScript language values, produces true or false.
|
||||||
|
/// Such a comparison is performed as follows:
|
||||||
|
///
|
||||||
|
/// https://tc39.es/ecma262/#sec-samevalue
|
||||||
|
/// strict mode currently compares the pointers
|
||||||
|
pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { |
||||||
|
if strict { |
||||||
|
// Do both Values point to the same underlying valueData?
|
||||||
|
return std::ptr::eq(x.data(), y.data()); |
||||||
|
} |
||||||
|
|
||||||
|
// 1. If Type(x) is different from Type(y), return false.
|
||||||
|
if x.get_type() != y.get_type() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
match (x.data(), y.data()) { |
||||||
|
// 2. If Type(x) is Number or BigInt, then
|
||||||
|
// a. Return ! Type(x)::SameValue(x, y).
|
||||||
|
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value(x, y), |
||||||
|
(ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value(*x, *y), |
||||||
|
(ValueData::Rational(x), ValueData::Integer(y)) => Number::same_value(*x, f64::from(*y)), |
||||||
|
(ValueData::Integer(x), ValueData::Rational(y)) => Number::same_value(f64::from(*x), *y), |
||||||
|
(ValueData::Integer(x), ValueData::Integer(y)) => x == y, |
||||||
|
|
||||||
|
// 3. Return ! SameValueNonNumeric(x, y).
|
||||||
|
(_, _) => same_value_non_numeric(x, y), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// The internal comparison abstract operation SameValueZero(x, y),
|
||||||
|
/// where x and y are ECMAScript language values, produces true or false.
|
||||||
|
/// SameValueZero differs from SameValue only in its treatment of +0 and -0.
|
||||||
|
///
|
||||||
|
/// Such a comparison is performed as follows:
|
||||||
|
///
|
||||||
|
/// <https://tc39.es/ecma262/#sec-samevaluezero>
|
||||||
|
pub fn same_value_zero(x: &Value, y: &Value) -> bool { |
||||||
|
if x.get_type() != y.get_type() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
match (x.data(), y.data()) { |
||||||
|
// 2. If Type(x) is Number or BigInt, then
|
||||||
|
// a. Return ! Type(x)::SameValueZero(x, y).
|
||||||
|
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value_zero(x, y), |
||||||
|
|
||||||
|
(ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value_zero(*x, *y), |
||||||
|
(ValueData::Rational(x), ValueData::Integer(y)) => { |
||||||
|
Number::same_value_zero(*x, f64::from(*y)) |
||||||
|
} |
||||||
|
(ValueData::Integer(x), ValueData::Rational(y)) => { |
||||||
|
Number::same_value_zero(f64::from(*x), *y) |
||||||
|
} |
||||||
|
(ValueData::Integer(x), ValueData::Integer(y)) => x == y, |
||||||
|
|
||||||
|
// 3. Return ! SameValueNonNumeric(x, y).
|
||||||
|
(_, _) => same_value_non_numeric(x, y), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn same_value_non_numeric(x: &Value, y: &Value) -> bool { |
||||||
|
debug_assert!(x.get_type() == y.get_type()); |
||||||
|
match x.get_type() { |
||||||
|
"undefined" => true, |
||||||
|
"null" => true, |
||||||
|
"string" => { |
||||||
|
if x.to_string() == y.to_string() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
false |
||||||
|
} |
||||||
|
"boolean" => bool::from(x) == bool::from(y), |
||||||
|
"object" => std::ptr::eq(x, y), |
||||||
|
_ => false, |
||||||
|
} |
||||||
|
} |
@ -1,246 +0,0 @@ |
|||||||
//! This module implements the `BigInt` structure, which represents the BigInt values in JavaScript.
|
|
||||||
//!
|
|
||||||
//! More information:
|
|
||||||
//! - [ECMAScript reference][spec]
|
|
||||||
//! - [MDN documentation][mdn]
|
|
||||||
//!
|
|
||||||
//! [spec]: https://tc39.es/ecma262/#sec-bigint-objects
|
|
||||||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
|
|
||||||
|
|
||||||
use gc::{unsafe_empty_trace, Finalize, Trace}; |
|
||||||
use num_traits::cast::{FromPrimitive, ToPrimitive}; |
|
||||||
use num_traits::pow::Pow; |
|
||||||
|
|
||||||
use std::convert::TryFrom; |
|
||||||
use std::str::FromStr; |
|
||||||
|
|
||||||
#[cfg(feature = "serde")] |
|
||||||
use serde::{Deserialize, Serialize}; |
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] |
|
||||||
pub struct BigInt(num_bigint::BigInt); |
|
||||||
|
|
||||||
impl BigInt { |
|
||||||
/// Returns the inner bigint structure.
|
|
||||||
#[inline] |
|
||||||
pub fn into_inner(self) -> num_bigint::BigInt { |
|
||||||
self.0 |
|
||||||
} |
|
||||||
|
|
||||||
/// Converts a string to a BigInt with the specified radix.
|
|
||||||
#[inline] |
|
||||||
pub fn from_str_radix(buf: &str, radix: u32) -> Option<Self> { |
|
||||||
num_bigint::BigInt::parse_bytes(buf.as_bytes(), radix).map(Self) |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub fn pow(self, other: &Self) -> Self { |
|
||||||
Self( |
|
||||||
self.0.pow( |
|
||||||
other |
|
||||||
.0 |
|
||||||
.to_biguint() |
|
||||||
.expect("RangeError: \"BigInt negative exponent\""), |
|
||||||
), |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
/// Converts the BigInt to a f64 type.
|
|
||||||
///
|
|
||||||
/// Returns `std::f64::INFINITY` if the BigInt is too big.
|
|
||||||
#[inline] |
|
||||||
pub fn to_f64(&self) -> f64 { |
|
||||||
self.0.to_f64().unwrap_or(std::f64::INFINITY) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<i64> for BigInt { |
|
||||||
fn from(n: i64) -> BigInt { |
|
||||||
BigInt(num_bigint::BigInt::from(n)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl From<i32> 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<f64> for BigInt { |
|
||||||
type Error = TryFromF64Error; |
|
||||||
|
|
||||||
fn try_from(n: f64) -> Result<Self, Self::Error> { |
|
||||||
match num_bigint::BigInt::from_f64(n) { |
|
||||||
Some(bigint) => Ok(BigInt(bigint)), |
|
||||||
None => Err(TryFromF64Error), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] |
|
||||||
pub struct ParseBigIntError; |
|
||||||
|
|
||||||
impl std::fmt::Display for ParseBigIntError { |
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
||||||
write!(f, "Could not parse to BigInt type") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl FromStr for BigInt { |
|
||||||
type Err = ParseBigIntError; |
|
||||||
|
|
||||||
fn from_str(string: &str) -> Result<Self, Self::Err> { |
|
||||||
match num_bigint::BigInt::from_str(string) { |
|
||||||
Ok(bigint) => Ok(BigInt(bigint)), |
|
||||||
Err(_) => Err(ParseBigIntError), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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::Shr for BigInt { |
|
||||||
type Output = Self; |
|
||||||
|
|
||||||
fn shr(mut self, other: Self) -> Self::Output { |
|
||||||
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) |
|
||||||
} |
|
||||||
|
|
||||||
return self; |
|
||||||
} |
|
||||||
|
|
||||||
panic!("RangeError: Maximum BigInt size exceeded"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl std::ops::Shl for BigInt { |
|
||||||
type Output = Self; |
|
||||||
|
|
||||||
fn shl(mut self, other: Self) -> Self::Output { |
|
||||||
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) |
|
||||||
} |
|
||||||
|
|
||||||
return self; |
|
||||||
} |
|
||||||
|
|
||||||
panic!("RangeError: Maximum BigInt size exceeded"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl std::ops::Neg for BigInt { |
|
||||||
type Output = Self; |
|
||||||
|
|
||||||
fn neg(self) -> Self::Output { |
|
||||||
Self(-self.0) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl PartialEq<i32> for BigInt { |
|
||||||
fn eq(&self, other: &i32) -> bool { |
|
||||||
self.0 == num_bigint::BigInt::from(*other) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl PartialEq<BigInt> for i32 { |
|
||||||
fn eq(&self, other: &BigInt) -> bool { |
|
||||||
num_bigint::BigInt::from(*self) == other.0 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl PartialEq<f64> 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<BigInt> for f64 { |
|
||||||
fn eq(&self, other: &BigInt) -> bool { |
|
||||||
if self.fract() != 0.0 { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
num_bigint::BigInt::from(*self as i64) == other.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) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl std::ops::Deref for BigInt { |
|
||||||
type Target = num_bigint::BigInt; |
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { |
|
||||||
&self.0 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl std::ops::DerefMut for BigInt { |
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target { |
|
||||||
&mut self.0 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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.
|
|
||||||
unsafe_empty_trace!(); |
|
||||||
} |
|
Loading…
Reference in new issue