use crate::{
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{RcBigInt, Value},
BoaProfiler, Context, Result,
};
#[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;
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BigInt(num_bigint::BigInt);
impl BuiltIn for BigInt {
const NAME: &'static str = "BigInt";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let bigint_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().bigint_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::to_string, "toString", 1)
.method(Self::value_of, "valueOf", 0)
.static_method(Self::as_int_n, "asIntN", 2)
.static_method(Self::as_uint_n, "asUintN", 2)
.callable(true)
.constructable(false)
.build();
(Self::NAME, bigint_object.into(), Self::attribute())
}
}
impl BigInt {
pub(crate) const LENGTH: usize = 1;
fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_bigint(context)?,
None => RcBigInt::from(Self::from(0)),
};
Ok(Value::from(data))
}
#[inline]
fn this_bigint_value(value: &Value, context: &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(context.construct_type_error("'this' is not a BigInt"))
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let radix = if !args.is_empty() {
args[0].to_integer(context)? as i32
} else {
10
};
if !(2..=36).contains(&radix) {
return context
.throw_range_error("radix must be an integer at least 2 and no greater than 36");
}
Ok(Value::from(
Self::this_bigint_value(this, context)?.to_string_radix(radix as u32),
))
}
pub(crate) fn value_of(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_bigint_value(this, context)?))
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_int_n(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
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")
{
Ok(Value::from(
modulo
- BigInt::from(2)
.pow(&BigInt::from(bits as i64))
.expect("the exponent must be positive"),
))
} else {
Ok(Value::from(modulo))
}
}
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_uint_n(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let (modulo, _) = Self::calculate_as_uint_n(args, context)?;
Ok(Value::from(modulo))
}
fn calculate_as_uint_n(args: &[Value], context: &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(context)?;
let bits = u32::try_from(bits).unwrap_or(u32::MAX);
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"),
),
bits,
))
}
}
impl Finalize for BigInt {}
unsafe impl Trace for BigInt {
empty_trace!();
}