use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
value::{RcBigInt, Value},
BoaProfiler, Context, Result,
};
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;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BigInt(num_bigint::BigInt);
impl BigInt {
pub(crate) const NAME: &'static str = "BigInt";
pub(crate) const LENGTH: usize = 1;
#[inline]
fn this_bigint_value(value: &Value, ctx: &mut Context) -> Result<RcBigInt> {
match value {
Value::BigInt(ref bigint) => return Ok(bigint.clone()),
Value::Object(ref object) => {
if let ObjectData::BigInt(ref bigint) = object.borrow().data {
return Ok(bigint.clone());
}
}
_ => {}
}
Err(ctx.construct_type_error("'this' is not a BigInt"))
}
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_bigint(ctx)?,
None => RcBigInt::from(Self::from(0)),
};
Ok(Value::from(data))
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let radix = if !args.is_empty() {
args[0].to_integer(ctx)? as i32
} else {
10
};
if radix < 2 && radix > 36 {
return ctx
.throw_range_error("radix must be an integer at least 2 and no greater than 36");
}
Ok(Value::from(
Self::this_bigint_value(this, ctx)?.to_string_radix(radix as u32),
))
}
pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_bigint_value(this, ctx)?))
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_int_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?;
if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) {
Ok(Value::from(
modulo - BigInt::from(2).pow(&BigInt::from(bits as i64)),
))
} else {
Ok(Value::from(modulo))
}
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_uint_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?;
Ok(Value::from(modulo))
}
fn calculate_as_uint_n(args: &[Value], ctx: &mut Context) -> Result<(BigInt, u32)> {
use std::convert::TryFrom;
let undefined_value = Value::undefined();
let bits_arg = args.get(0).unwrap_or(&undefined_value);
let bigint_arg = args.get(1).unwrap_or(&undefined_value);
let bits = bits_arg.to_index(ctx)?;
let bits = u32::try_from(bits).unwrap_or(u32::MAX);
let bigint = bigint_arg.to_bigint(ctx)?;
Ok((
bigint
.as_inner()
.clone()
.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))),
bits,
))
}
#[inline]
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::to_string, "toString", &prototype, 1, interpreter);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0, interpreter);
let bigint_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_bigint,
global,
prototype,
false,
true,
);
make_builtin_fn(Self::as_int_n, "asIntN", &bigint_object, 2, interpreter);
make_builtin_fn(Self::as_uint_n, "asUintN", &bigint_object, 2, interpreter);
(Self::NAME, bigint_object)
}
}
impl Finalize for BigInt {}
unsafe impl Trace for BigInt {
unsafe_empty_trace!();
}