Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(language): implement more bigint ops #63

Merged
merged 9 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use crate::ecmascript::{
execution::{agent::JsError, Agent, JsResult},
types::{bigint, Number, Value},
types::{
bigint::{self, BigInt},
Number, Value,
},
};

use super::type_conversion::{to_primitive, PreferredType};
Expand Down Expand Up @@ -249,7 +252,7 @@ pub(crate) fn is_less_than<const LEFT_FIRST: bool>(
// 2. Return BigInt::lessThan(nx, ny).
let nx = nx.to_bigint(agent)?;
let ny = ny.to_bigint(agent)?;
return Ok(Some(bigint::less_than(agent, nx, ny)));
return Ok(Some(BigInt::less_than(agent, nx, ny)));
}
}

Expand Down
184 changes: 180 additions & 4 deletions nova_vm/src/ecmascript/types/language/bigint.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,191 @@
mod abstract_operations;
mod data;

use super::value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT};
use crate::{heap::indexes::BigIntIndex, SmallInteger};
use super::{
value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT},
Value,
};
use crate::{
ecmascript::execution::{agent::ExceptionType, Agent, JsResult},
heap::{indexes::BigIntIndex, CreateHeapData, GetHeapData},
SmallInteger,
};

pub use abstract_operations::*;
pub use data::BigIntHeapData;

/// [6.1.6.2 The BigInt Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type)
///
/// The BigInt type represents an integer value. The value may be any size and
/// is not limited to a particular bit-width. Generally, where not otherwise
/// noted, operations are designed to return exact mathematically-based answers.
/// For binary operations, BigInts act as two's complement binary strings, with
/// negative numbers treated as having bits set infinitely to the left.
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum BigInt {
sno2 marked this conversation as resolved.
Show resolved Hide resolved
BigInt(BigIntIndex) = BIGINT_DISCRIMINANT,
SmallBigInt(SmallInteger) = SMALL_BIGINT_DISCRIMINANT,
sno2 marked this conversation as resolved.
Show resolved Hide resolved
}

impl BigInt {
/// ### [6.1.6.2.1 BigInt::unaryMinus ( x )](https://tc39.es/ecma262/#sec-numeric-types-bigint-unaryMinus)
///
/// The abstract operation BigInt::unaryMinus takes argument x (a BigInt)
/// and returns a BigInt.
pub(crate) fn unary_minus(agent: &mut Agent, x: BigInt) -> BigInt {
// 1. If x is 0ℤ, return 0ℤ.
// NOTE: This is handled with the negation below.

// 2. Return -x.
match x {
BigInt::SmallBigInt(x) => {
// We need to check if the negation will overflow.
if x.into_i64() != SmallInteger::MAX_BIGINT {
BigInt::SmallBigInt(-x)
} else {
agent.heap.create(BigIntHeapData {
data: -num_bigint_dig::BigInt::from(x.into_i64()),
})
}
}
BigInt::BigInt(x_index) => {
let x_data = agent.heap.get(x_index);
agent.heap.create(BigIntHeapData {
data: -&x_data.data,
})
}
}
}

/// ### [6.1.6.2.2 BigInt::bitwiseNOT ( x )](https://tc39.es/ecma262/#sec-numeric-types-bigint-bitwiseNOT)
///
/// The abstract operation BigInt::bitwiseNOT takes argument x (a BigInt)
/// and returns a BigInt. It returns the one's complement of x.
pub(crate) fn bitwise_not(agent: &mut Agent, x: BigInt) -> BigInt {
// 1. Return -x - 1ℤ.
// NOTE: We can use the builtin bitwise not operations instead.
match x {
BigInt::SmallBigInt(x) => BigInt::SmallBigInt(!x),
BigInt::BigInt(x_index) => {
let x_data = agent.heap.get(x_index);
agent.heap.create(BigIntHeapData {
data: -&x_data.data,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
data: -&x_data.data,
data: !&x_data.data,

Bit late, my apologies

})
}
}
}

/// ### [6.1.6.2.3 BigInt::exponentiate ( base, exponent )](https://tc39.es/ecma262/#sec-numeric-types-bigint-exponentiate)
///
/// The abstract operation BigInt::exponentiate takes arguments base (a
/// BigInt) and exponent (a BigInt) and returns either a normal completion
/// containing a BigInt or a throw completion.
pub(crate) fn exponentiate(
agent: &mut Agent,
base: BigInt,
exponent: BigInt,
) -> JsResult<BigInt> {
// 1. If exponent < 0ℤ, throw a RangeError exception.
if match exponent {
BigInt::SmallBigInt(x) if x.into_i64() < 0 => true,
BigInt::BigInt(x) => agent.heap.get(x).data < 0.into(),
_ => false,
} {
return Err(
agent.throw_exception(ExceptionType::RangeError, "exponent must be positive")
);
}

// TODO: 2. If base is 0ℤ and exponent is 0ℤ, return 1ℤ.
// TODO: 3. Return base raised to the power exponent.
// NOTE: The BigInt implementation does not support native
// exponentiation.

Err(agent.throw_exception(ExceptionType::EvalError, "Unsupported operation."))
sno2 marked this conversation as resolved.
Show resolved Hide resolved
}

/// ### [6.1.6.2.4 BigInt::multiply ( x, y )](https://tc39.es/ecma262/#sec-numeric-types-bigint-multiply)
///
/// The abstract operation BigInt::multiply takes arguments x (a BigInt) and
/// y (a BigInt) and returns a BigInt.
pub(crate) fn multiply(agent: &mut Agent, x: BigInt, y: BigInt) -> BigInt {
match (x, y) {
(BigInt::SmallBigInt(x), BigInt::SmallBigInt(y)) => {
let (x, y) = (x.into_i64() as i128, y.into_i64() as i128);
let result = x * y;

if let Ok(result) = SmallInteger::try_from(result) {
BigInt::SmallBigInt(SmallInteger::try_from(result).unwrap())
} else {
agent.heap.create(BigIntHeapData {
data: result.into(),
})
}
}
(BigInt::SmallBigInt(x), BigInt::BigInt(y))
| (BigInt::BigInt(y), BigInt::SmallBigInt(x)) => {
let x = x.into_i64();
let y = agent.heap.get(y);
agent.heap.create(BigIntHeapData { data: x * &y.data })
}
(BigInt::BigInt(x), BigInt::BigInt(y)) => {
let (x, y) = (agent.heap.get(x), agent.heap.get(y));
agent.heap.create(BigIntHeapData {
data: &x.data * &y.data,
})
}
}
}

/// ### [6.1.6.2.12 BigInt::lessThan ( x, y )](https://tc39.es/ecma262/#sec-numeric-types-bigint-lessThan)
///
/// The abstract operation BigInt::lessThan takes arguments x (a BigInt) and
/// y (a BigInt) and returns a Boolean.
pub(crate) fn less_than(agent: &mut Agent, x: BigInt, y: BigInt) -> bool {
// 1. If ℝ(x) < ℝ(y), return true; otherwise return false.
match (x, y) {
(BigInt::BigInt(_), BigInt::SmallBigInt(_)) => false,
(BigInt::SmallBigInt(_), BigInt::BigInt(_)) => true,
(BigInt::BigInt(b1), BigInt::BigInt(b2)) => {
let (b1, b2) = (agent.heap.get(b1), agent.heap.get(b2));
b1.data < b2.data
}
(BigInt::SmallBigInt(b1), BigInt::SmallBigInt(b2)) => b1.into_i64() < b2.into_i64(),
}
}

/// ### [6.1.6.2.13 BigInt::equal ( x, y )](https://tc39.es/ecma262/#sec-numeric-types-bigint-equal)
///
/// The abstract operation BigInt::equal takes arguments x (a BigInt) and y (a
/// BigInt) and returns a Boolean.
pub(crate) fn equal(agent: &mut Agent, x: BigInt, y: BigInt) -> bool {
// 1. If ℝ(x) = ℝ(y), return true; otherwise return false.
match (x, y) {
(BigInt::BigInt(x), BigInt::BigInt(y)) => {
let (x, y) = (agent.heap.get(x), agent.heap.get(y));
x.data == y.data
}
(BigInt::SmallBigInt(x), BigInt::SmallBigInt(y)) => x == y,
_ => false,
}
}
}

impl TryFrom<Value> for BigInt {
type Error = ();
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::BigInt(x) => Ok(BigInt::BigInt(x)),
Value::SmallBigInt(x) => Ok(BigInt::SmallBigInt(x)),
sno2 marked this conversation as resolved.
Show resolved Hide resolved
_ => Err(()),
}
}
}

impl From<BigInt> for Value {
fn from(value: BigInt) -> Value {
match value {
BigInt::BigInt(x) => Value::BigInt(x),
BigInt::SmallBigInt(x) => Value::SmallBigInt(x),
sno2 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

This file was deleted.

30 changes: 30 additions & 0 deletions nova_vm/src/engine/small_integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ impl SmallInteger {
}
}

impl std::ops::Neg for SmallInteger {
type Output = Self;

/// ## Panics
/// - If the negation overflows.
fn neg(self) -> Self::Output {
Self::from_i64_unchecked(-self.into_i64())
}
}

impl std::ops::Not for SmallInteger {
type Output = Self;
fn not(self) -> Self::Output {
// NOTE: This is safe because the bitwise not of any number in the range
// will always be in the safe number range.
Self::from_i64_unchecked(!self.into_i64())
sno2 marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl TryFrom<i64> for SmallInteger {
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
Expand All @@ -57,6 +76,17 @@ impl TryFrom<i64> for SmallInteger {
}
}

impl TryFrom<i128> for SmallInteger {
type Error = ();
fn try_from(value: i128) -> Result<Self, Self::Error> {
if (Self::MIN_BIGINT as i128..=Self::MAX_BIGINT as i128).contains(&value) {
Ok(Self::from_i64_unchecked(value as i64))
} else {
Err(())
}
}
}

impl TryFrom<u64> for SmallInteger {
type Error = ();
fn try_from(value: u64) -> Result<Self, Self::Error> {
Expand Down
13 changes: 10 additions & 3 deletions nova_vm/src/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use self::{
heap_constants::{
FIRST_CONSTRUCTOR_INDEX, LAST_BUILTIN_OBJECT_INDEX, LAST_WELL_KNOWN_SYMBOL_INDEX,
},
indexes::{BaseIndex, FunctionIndex, NumberIndex, ObjectIndex, StringIndex},
indexes::{BaseIndex, BigIntIndex, FunctionIndex, NumberIndex, ObjectIndex, StringIndex},
math::initialize_math_object,
number::initialize_number_heap,
object::{initialize_object_heap, ObjectEntry, PropertyDescriptor},
Expand All @@ -46,8 +46,8 @@ use crate::ecmascript::{
builtins::{ArrayBufferHeapData, ArrayHeapData},
execution::{Environments, Realm, RealmIdentifier},
types::{
BigIntHeapData, Function, FunctionHeapData, Number, NumberHeapData, Object, ObjectHeapData,
PropertyKey, String, StringHeapData, Value,
BigInt, BigIntHeapData, Function, FunctionHeapData, Number, NumberHeapData, Object,
ObjectHeapData, PropertyKey, String, StringHeapData, Value,
},
};
use wtf8::{Wtf8, Wtf8Buf};
Expand Down Expand Up @@ -173,6 +173,13 @@ impl CreateHeapData<ObjectHeapData, Object> for Heap<'_, '_> {
}
}

impl CreateHeapData<BigIntHeapData, BigInt> for Heap<'_, '_> {
fn create(&mut self, data: BigIntHeapData) -> BigInt {
self.bigints.push(Some(data));
BigInt::BigInt(BigIntIndex::last(&self.bigints))
}
}

impl<'ctx, 'host> Heap<'ctx, 'host> {
pub fn new() -> Heap<'ctx, 'host> {
let mut heap = Heap {
Expand Down