Skip to content

Commit

Permalink
feat(vm): implement missing BigInt functions (#384)
Browse files Browse the repository at this point in the history
Co-authored-by: Andreu Botella <[email protected]>
Co-authored-by: Aapo Alasuutari <[email protected]>
  • Loading branch information
3 people authored Sep 8, 2024
1 parent f52a0a0 commit 21313ea
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use num_bigint::ToBigInt;
use num_traits::Pow;

use crate::ecmascript::abstract_operations::testing_and_comparison::is_integral_number;
use crate::ecmascript::abstract_operations::type_conversion::to_big_int;
Expand Down Expand Up @@ -53,7 +54,7 @@ impl Builtin for BigIntAsIntN {
struct BigIntAsUintN;
impl Builtin for BigIntAsUintN {
const BEHAVIOUR: Behaviour = Behaviour::Regular(BigIntConstructor::as_uint_n);
const LENGTH: u8 = 1;
const LENGTH: u8 = 2;
const NAME: String = BUILTIN_STRING_MEMORY.asUintN;
}

Expand Down Expand Up @@ -92,16 +93,64 @@ impl BigIntConstructor {
));
};
let bigint = to_big_int(agent, arguments.get(1))?;
match bigint {
BigInt::BigInt(_) => todo!(),
BigInt::SmallBigInt(int) => {
let int = int.into_i64();
let modulo = int % 2i64.pow(bits);
if modulo >= 2i64.pow(bits - 1) {
let result = modulo - 2i64.pow(bits);
Ok(BigInt::from(SmallBigInt::try_from(result).unwrap()).into_value())
} else {
Ok(BigInt::from(SmallBigInt::try_from(modulo).unwrap()).into_value())
if bits == 0 {
return Ok(BigInt::zero().into_value());
}

match 2i64.checked_pow(bits) {
Some(divisor) => {
match bigint {
BigInt::BigInt(bigint) => {
let modulo = &agent[bigint].data % divisor;
// SAFETY: This cannot overflow since 2^bits didn't.
let divisor_half = divisor >> 1;
if let Ok(modulo) = i64::try_from(&modulo) {
let modulo = if modulo >= divisor_half {
modulo - divisor
} else {
modulo
};
Ok(BigInt::from_i64(agent, modulo).into_value())
} else {
Ok(BigInt::from_num_bigint(agent, modulo - divisor).into_value())
}
}
BigInt::SmallBigInt(bigint) => {
let bigint = bigint.into_i64();
let modulo = bigint % divisor;
let modulo = if modulo >= 2i64.pow(bits - 1) {
modulo - divisor
} else {
modulo
};
Ok(BigInt::from(SmallBigInt::try_from(modulo).unwrap()).into_value())
}
}
}
_ => {
let divisor =
num_bigint::BigInt::from_bytes_le(num_bigint::Sign::Plus, &[2]).pow(bits);
match bigint {
BigInt::BigInt(bigint) => {
let modulo = &agent[bigint].data % &divisor;
let divisor_half = &divisor >> 1;
if let Ok(modulo) = i64::try_from(&modulo) {
// Maybe safe? Maybe not.
Ok(BigInt::from_i64(agent, modulo).into_value())
} else {
let modulo = if modulo >= divisor_half {
modulo - divisor
} else {
modulo
};
Ok(BigInt::from_num_bigint(agent, modulo).into_value())
}
}
BigInt::SmallBigInt(_) => {
// Probably safe: The divisor is bigger than i64 but
// value is i54.
Ok(bigint.into_value())
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use crate::{
ecmascript::{
builders::ordinary_object_builder::OrdinaryObjectBuilder,
builtins::{ArgumentsList, Builtin},
builtins::{primitive_objects::PrimitiveObjectData, ArgumentsList, Builtin},
execution::{agent::ExceptionType, Agent, JsResult, RealmIdentifier},
types::{BigInt, IntoValue, String, Value, BUILTIN_STRING_MEMORY},
},
Expand Down Expand Up @@ -97,11 +97,27 @@ impl BigIntPrototype {
}
}

/// ### [21.2.3.4.1 ThisBigIntValue ( value )](https://tc39.es/ecma262/#sec-thisbigintvalue)
///
/// The abstract operation ThisBigIntValue takes argument value (an ECMAScript
/// language value) and returns either a normal completion containing a BigInt
/// or a throw completion.
fn this_big_int_value(agent: &mut Agent, value: Value) -> JsResult<BigInt> {
match value {
Value::BigInt(idx) => Ok(idx.into()),
Value::SmallBigInt(data) => Ok(data.into()),
// TODO: Primitive objects
// 1. If value is a BigInt, return value.
Value::BigInt(value) => Ok(value.into()),
Value::SmallBigInt(value) => Ok(value.into()),
// 2. If value is an Object and value has a [[BigIntData]] internal slot, then
Value::PrimitiveObject(value) if value.is_bigint_object(agent) => {
match agent[value].data {
// b. Return value.[[BigIntData]].
PrimitiveObjectData::BigInt(value) => Ok(value.into()),
PrimitiveObjectData::SmallBigInt(value) => Ok(value.into()),
// a. Assert: value.[[BigIntData]] is a BigInt.
_ => unreachable!(),
}
}
// 3. Throw a TypeError exception.
_ => {
Err(agent.throw_exception_with_static_message(ExceptionType::TypeError, "Not a BigInt"))
}
Expand Down
Loading

0 comments on commit 21313ea

Please sign in to comment.