From e3b1923adb4a82415d562ac635e85f363bb45bd2 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Wed, 1 Nov 2023 14:53:09 -0500 Subject: [PATCH 1/9] feat(language): implement more bigint ops --- .../testing_and_comparison.rs | 7 +- .../src/ecmascript/types/language/bigint.rs | 160 +++++++++++++++++- .../language/bigint/abstract_operations.rs | 22 --- nova_vm/src/engine/small_integer.rs | 18 ++ nova_vm/src/heap.rs | 13 +- 5 files changed, 190 insertions(+), 30 deletions(-) delete mode 100644 nova_vm/src/ecmascript/types/language/bigint/abstract_operations.rs diff --git a/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs b/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs index 2318b62c..1c97ec62 100644 --- a/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs +++ b/nova_vm/src/ecmascript/abstract_operations/testing_and_comparison.rs @@ -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}; @@ -249,7 +252,7 @@ pub(crate) fn is_less_than( // 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))); } } diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 25c1d1b2..454e0a18 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -1,15 +1,169 @@ -mod abstract_operations; mod data; use super::value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT}; -use crate::{heap::indexes::BigIntIndex, SmallInteger}; +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 { BigInt(BigIntIndex) = BIGINT_DISCRIMINANT, SmallBigInt(SmallInteger) = SMALL_BIGINT_DISCRIMINANT, } + +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) => BigInt::SmallBigInt(-x), + BigInt::BigInt(x) => { + let x = agent.heap.get(x); + agent.heap.create(BigIntHeapData { data: -&x.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) => { + let x = agent.heap.get(x); + agent.heap.create(BigIntHeapData { data: !&x.data }) + } + } + } + + /// ### [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 { + // 1. If exponent < 0ℤ, throw a RangeError exception. + match exponent { + BigInt::SmallBigInt(x) if x.into_i64() < 0 => { + return Err( + agent.throw_exception(ExceptionType::RangeError, "exponent must be positive") + ); + } + BigInt::BigInt(x) => { + let x = agent.heap.get(x); + if x.data < 0.into() { + 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.")) + } + + /// ### [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(), y.into_i64()); + if let Some(result) = x.checked_mul(y) { + if let Ok(result) = SmallInteger::try_from(result) { + BigInt::SmallBigInt(SmallInteger::try_from(result).unwrap()) + } else { + agent.heap.create(BigIntHeapData { + data: result.into(), + }) + } + } else { + agent.heap.create(BigIntHeapData { + data: num_bigint_dig::BigInt::from(x) * num_bigint_dig::BigInt::from(y), + }) + } + } + (BigInt::SmallBigInt(x), BigInt::BigInt(y)) => { + let x = x.into_i64(); + let y = agent.heap.get(y); + agent.heap.create(BigIntHeapData { data: x * &y.data }) + } + (BigInt::BigInt(x), BigInt::SmallBigInt(y)) => { + let x = agent.heap.get(x); + let y = y.into_i64(); + agent.heap.create(BigIntHeapData { data: &x.data * y }) + } + (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.into_i64() == y.into_i64(), + _ => false, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/bigint/abstract_operations.rs b/nova_vm/src/ecmascript/types/language/bigint/abstract_operations.rs deleted file mode 100644 index 9c7fbb2b..00000000 --- a/nova_vm/src/ecmascript/types/language/bigint/abstract_operations.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{ - ecmascript::{execution::Agent, types::BigInt}, - heap::GetHeapData, -}; - -/// ### [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. It performs the following steps when -/// called: -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(), - } -} diff --git a/nova_vm/src/engine/small_integer.rs b/nova_vm/src/engine/small_integer.rs index 3af23ec8..1bc3398f 100644 --- a/nova_vm/src/engine/small_integer.rs +++ b/nova_vm/src/engine/small_integer.rs @@ -46,6 +46,24 @@ impl SmallInteger { } } +impl std::ops::Neg for SmallInteger { + type Output = Self; + fn neg(self) -> Self::Output { + // NOTE: This is safe because the negation of any number in the range + // will always be in the safe number range. + 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()) + } +} + impl TryFrom for SmallInteger { type Error = (); fn try_from(value: i64) -> Result { diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index f583e591..ac963a7e 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -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}, @@ -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}; @@ -173,6 +173,13 @@ impl CreateHeapData for Heap<'_, '_> { } } +impl CreateHeapData 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 { From b210321a4429d95b198015e13c6fa437d941b502 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 12:44:23 -0500 Subject: [PATCH 2/9] add From for Value and TryFrom for BigInt --- .../src/ecmascript/types/language/bigint.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 454e0a18..a2052658 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -1,6 +1,9 @@ mod data; -use super::value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT}; +use super::{ + value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT}, + Value, +}; use crate::{ ecmascript::execution::{agent::ExceptionType, Agent, JsResult}, heap::{indexes::BigIntIndex, CreateHeapData, GetHeapData}, @@ -167,3 +170,23 @@ impl BigInt { } } } + +impl TryFrom for BigInt { + type Error = (); + fn try_from(value: Value) -> Result { + match value { + Value::BigInt(x) => Ok(BigInt::BigInt(x)), + Value::SmallBigInt(x) => Ok(BigInt::SmallBigInt(x)), + _ => Err(()), + } + } +} + +impl From for Value { + fn from(value: BigInt) -> Value { + match value { + BigInt::BigInt(x) => Value::BigInt(x), + BigInt::SmallBigInt(x) => Value::SmallBigInt(x), + } + } +} From a1173b09521303aa0550b52697cfc2578f484c96 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 12:46:10 -0500 Subject: [PATCH 3/9] use SmallInteger PartialEq impl --- nova_vm/src/ecmascript/types/language/bigint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index a2052658..eb3f7d6d 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -165,7 +165,7 @@ impl BigInt { let (x, y) = (agent.heap.get(x), agent.heap.get(y)); x.data == y.data } - (BigInt::SmallBigInt(x), BigInt::SmallBigInt(y)) => x.into_i64() == y.into_i64(), + (BigInt::SmallBigInt(x), BigInt::SmallBigInt(y)) => x == y, _ => false, } } From 67c825ce9bf2ca82f73cb3a9649f53b23d7964e4 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 19:16:17 -0500 Subject: [PATCH 4/9] Update nova_vm/src/ecmascript/types/language/bigint.rs Co-authored-by: vimirage --- .../src/ecmascript/types/language/bigint.rs | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index eb3f7d6d..988b34df 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -72,20 +72,15 @@ impl BigInt { exponent: BigInt, ) -> JsResult { // 1. If exponent < 0ℤ, throw a RangeError exception. - match exponent { - BigInt::SmallBigInt(x) if x.into_i64() < 0 => { - return Err( - agent.throw_exception(ExceptionType::RangeError, "exponent must be positive") - ); - } - BigInt::BigInt(x) => { - let x = agent.heap.get(x); - if x.data < 0.into() { - return Err(agent - .throw_exception(ExceptionType::RangeError, "exponent must be positive")); - } - } - _ => {} + 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ℤ. From 644b8e7e580446c5dd6586778cf5baa0b36e0c81 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 19:23:17 -0500 Subject: [PATCH 5/9] Update nova_vm/src/ecmascript/types/language/bigint.rs Co-authored-by: vimirage --- nova_vm/src/ecmascript/types/language/bigint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 988b34df..86b385ec 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -74,7 +74,7 @@ impl 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() + BigInt::BigInt(x) => agent.heap.get(x).data < 0.into(), _ => false } { return Err( From 76f50cc6d2d916370c720faf0f1d7abab6ab7e66 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 19:30:54 -0500 Subject: [PATCH 6/9] Update nova_vm/src/ecmascript/types/language/bigint.rs Co-authored-by: vimirage --- nova_vm/src/ecmascript/types/language/bigint.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 86b385ec..b08ed154 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -81,7 +81,6 @@ impl BigInt { 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. From f7d7b7e12b7b6a0d4ee539ef9447220e45243862 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 2 Nov 2023 19:41:02 -0500 Subject: [PATCH 7/9] address some comments --- .../src/ecmascript/types/language/bigint.rs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index b08ed154..6696e0ba 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -38,9 +38,11 @@ impl BigInt { // 2. Return -x. match x { BigInt::SmallBigInt(x) => BigInt::SmallBigInt(-x), - BigInt::BigInt(x) => { - let x = agent.heap.get(x); - agent.heap.create(BigIntHeapData { data: -&x.data }) + BigInt::BigInt(x_index) => { + let x_data = agent.heap.get(x_index); + agent.heap.create(BigIntHeapData { + data: -&x_data.data, + }) } } } @@ -54,9 +56,11 @@ impl BigInt { // NOTE: We can use the builtin bitwise not operations instead. match x { BigInt::SmallBigInt(x) => BigInt::SmallBigInt(!x), - BigInt::BigInt(x) => { - let x = agent.heap.get(x); - agent.heap.create(BigIntHeapData { data: !&x.data }) + BigInt::BigInt(x_index) => { + let x_data = agent.heap.get(x_index); + agent.heap.create(BigIntHeapData { + data: -&x_data.data, + }) } } } @@ -75,10 +79,10 @@ impl BigInt { if match exponent { BigInt::SmallBigInt(x) if x.into_i64() < 0 => true, BigInt::BigInt(x) => agent.heap.get(x).data < 0.into(), - _ => false - } { + _ => false, + } { return Err( - agent.throw_exception(ExceptionType::RangeError, "exponent must be positive") + agent.throw_exception(ExceptionType::RangeError, "exponent must be positive") ); } @@ -112,16 +116,12 @@ impl BigInt { }) } } - (BigInt::SmallBigInt(x), BigInt::BigInt(y)) => { + (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::SmallBigInt(y)) => { - let x = agent.heap.get(x); - let y = y.into_i64(); - agent.heap.create(BigIntHeapData { data: &x.data * y }) - } (BigInt::BigInt(x), BigInt::BigInt(y)) => { let (x, y) = (agent.heap.get(x), agent.heap.get(y)); agent.heap.create(BigIntHeapData { From f07b8f6b0d491d2146b12b3dc42948dbe11f8143 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Fri, 3 Nov 2023 18:00:11 -0500 Subject: [PATCH 8/9] cleanup multiplication --- nova_vm/src/ecmascript/types/language/bigint.rs | 16 ++++++---------- nova_vm/src/engine/small_integer.rs | 11 +++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 6696e0ba..692798ab 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -101,18 +101,14 @@ impl 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(), y.into_i64()); - if let Some(result) = x.checked_mul(y) { - if let Ok(result) = SmallInteger::try_from(result) { - BigInt::SmallBigInt(SmallInteger::try_from(result).unwrap()) - } else { - agent.heap.create(BigIntHeapData { - data: result.into(), - }) - } + 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: num_bigint_dig::BigInt::from(x) * num_bigint_dig::BigInt::from(y), + data: result.into(), }) } } diff --git a/nova_vm/src/engine/small_integer.rs b/nova_vm/src/engine/small_integer.rs index 1bc3398f..772dce5c 100644 --- a/nova_vm/src/engine/small_integer.rs +++ b/nova_vm/src/engine/small_integer.rs @@ -75,6 +75,17 @@ impl TryFrom for SmallInteger { } } +impl TryFrom for SmallInteger { + type Error = (); + fn try_from(value: i128) -> Result { + 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 for SmallInteger { type Error = (); fn try_from(value: u64) -> Result { From 0888444a602cd289b03ffc5c8d34e89e030612cc Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Fri, 3 Nov 2023 22:10:09 -0500 Subject: [PATCH 9/9] fix negation ops panics --- nova_vm/src/ecmascript/types/language/bigint.rs | 11 ++++++++++- nova_vm/src/engine/small_integer.rs | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 692798ab..80acf733 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -37,7 +37,16 @@ impl BigInt { // 2. Return -x. match x { - BigInt::SmallBigInt(x) => BigInt::SmallBigInt(-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 { diff --git a/nova_vm/src/engine/small_integer.rs b/nova_vm/src/engine/small_integer.rs index 772dce5c..d202ac60 100644 --- a/nova_vm/src/engine/small_integer.rs +++ b/nova_vm/src/engine/small_integer.rs @@ -48,9 +48,10 @@ impl SmallInteger { impl std::ops::Neg for SmallInteger { type Output = Self; + + /// ## Panics + /// - If the negation overflows. fn neg(self) -> Self::Output { - // NOTE: This is safe because the negation of any number in the range - // will always be in the safe number range. Self::from_i64_unchecked(-self.into_i64()) } }