Skip to content

Commit

Permalink
Merge a700670 into bb2b6f6
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Jun 2, 2020
2 parents bb2b6f6 + a700670 commit 2be3d4d
Show file tree
Hide file tree
Showing 17 changed files with 621 additions and 542 deletions.
87 changes: 87 additions & 0 deletions boa/src/builtins/bigint/conversions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use super::BigInt;

use crate::{builtins::value::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> {
match num_bigint::BigInt::from_f64(n) {
Some(bigint) => Ok(BigInt(bigint)),
None => Err(TryFromF64Error),
}
}
}
74 changes: 74 additions & 0 deletions boa/src/builtins/bigint/equality.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
103 changes: 66 additions & 37 deletions boa/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,66 @@
use crate::{
builtins::{
function::{make_builtin_fn, make_constructor_fn},
value::{ResultValue, Value},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
syntax::ast::bigint::BigInt as AstBigInt,
};

use gc::{unsafe_empty_trace, Finalize, Trace};

#[cfg(feature = "serde")]
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.
#[derive(Debug, Clone, Copy)]
pub(crate) struct BigInt;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BigInt(num_bigint::BigInt);

impl BigInt {
/// The abstract operation thisBigIntValue takes argument value.
///
/// The phrase “this BigInt value” within the specification of a method refers to the
/// result returned by calling the abstract operation thisBigIntValue with the `this` value
/// of the method invocation passed as the argument.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue
#[inline]
fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result<Self, Value> {
match value.data() {
// 1. If Type(value) is BigInt, return value.
ValueData::BigInt(ref bigint) => return Ok(bigint.clone()),

// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
// a. Assert: Type(value.[[BigIntData]]) is BigInt.
// b. Return value.[[BigIntData]].
ValueData::Object(_) => {
let bigint = value.get_internal_slot("BigIntData");
if let ValueData::BigInt(bigint) = bigint.data() {
return Ok(bigint.clone());
}
}
_ => {}
}

// 3. Throw a TypeError exception.
ctx.throw_type_error("'this' is not a BigInt")?;
unreachable!();
}

/// `BigInt()`
///
/// The `BigInt()` constructor is used to create BigInt objects.
Expand All @@ -45,34 +91,12 @@ impl BigInt {
ctx: &mut Interpreter,
) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => {
if let Some(bigint) = value.to_bigint() {
Value::from(bigint)
} else {
let message = format!(
"{} can't be converted to BigInt because it isn't an integer",
ctx.to_string(value)?
);
return ctx.throw_range_error(message);
}
}
None => Value::from(AstBigInt::from(0)),
Some(ref value) => Value::from(ctx.to_bigint(value)?),
None => Value::from(Self::from(0)),
};
Ok(data)
}

#[inline]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_native_string_radix(bigint: &AstBigInt, radix: u32) -> String {
bigint.to_str_radix(radix)
}

#[inline]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_native_string(bigint: &AstBigInt) -> String {
bigint.to_string()
}

/// `BigInt.prototype.toString( [radix] )`
///
/// The `toString()` method returns a string representing the specified BigInt object.
Expand All @@ -98,10 +122,9 @@ impl BigInt {
return ctx
.throw_range_error("radix must be an integer at least 2 and no greater than 36");
}
Ok(Value::from(Self::to_native_string_radix(
&this.to_bigint().unwrap(),
radix as u32,
)))
Ok(Value::from(
Self::this_bigint_value(this, ctx)?.to_string_radix(radix as u32),
))
}

/// `BigInt.prototype.valueOf()`
Expand All @@ -117,17 +140,15 @@ impl BigInt {
pub(crate) fn value_of(
this: &mut Value,
_args: &[Value],
_ctx: &mut Interpreter,
ctx: &mut Interpreter,
) -> ResultValue {
Ok(Value::from(
this.to_bigint().expect("BigInt.prototype.valueOf"),
))
Ok(Value::from(Self::this_bigint_value(this, ctx)?))
}

/// Create a new `Number` object
pub(crate) fn create(global: &Value) -> Value {
let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("BigIntData", Value::from(AstBigInt::from(0)));
prototype.set_internal_slot("BigIntData", Value::from(Self::from(0)));

make_builtin_fn(Self::to_string, "toString", &prototype, 1);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
Expand All @@ -141,3 +162,11 @@ impl BigInt {
global.set_field("BigInt", Self::create(global));
}
}

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

0 comments on commit 2be3d4d

Please sign in to comment.