Skip to content

Commit

Permalink
Fix some panics related to BigInt operations (#884)
Browse files Browse the repository at this point in the history
* Fix some panics related to BigInt operations

* Address review comments

* Address review comments
  • Loading branch information
georgeroman authored Oct 18, 2020
1 parent 09d1889 commit f2f2153
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 73 deletions.
21 changes: 15 additions & 6 deletions boa/src/builtins/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,17 @@ impl BigInt {
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)) {
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)),
modulo
- BigInt::from(2)
.pow(&BigInt::from(bits as i64))
.expect("the exponent must be positive"),
))
} else {
Ok(Value::from(modulo))
Expand Down Expand Up @@ -214,10 +222,11 @@ impl BigInt {
let bigint = bigint_arg.to_bigint(ctx)?;

Ok((
bigint
.as_inner()
.clone()
.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))),
bigint.as_inner().clone().mod_floor(
&BigInt::from(2)
.pow(&BigInt::from(bits as i64))
.expect("the exponent must be positive"),
),
bits,
))
}
Expand Down
91 changes: 40 additions & 51 deletions boa/src/builtins/bigint/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,46 @@ use super::BigInt;

impl BigInt {
#[inline]
pub fn pow(self, other: &Self) -> Self {
Self(
self.0.pow(
other
.0
.to_biguint()
.expect("RangeError: \"BigInt negative exponent\""),
),
)
pub fn pow(self, other: &Self) -> Result<Self, String> {
Ok(Self(self.0.pow(
other.0.to_biguint().ok_or("BigInt negative exponent")?,
)))
}

#[inline]
pub fn shift_right(mut self, other: Self) -> Result<Self, String> {
use std::ops::ShlAssign;
use std::ops::ShrAssign;

if let Some(n) = other.0.to_i32() {
if n > 0 {
self.0.shr_assign(n as usize)
} else {
self.0.shl_assign(n.abs() as usize)
}

Ok(self)
} else {
Err("Maximum BigInt size exceeded".into())
}
}

#[inline]
pub fn shift_left(mut self, other: Self) -> Result<Self, String> {
use std::ops::ShlAssign;
use std::ops::ShrAssign;

if let Some(n) = other.0.to_i32() {
if n > 0 {
self.0.shl_assign(n as usize)
} else {
self.0.shr_assign(n.abs() as usize)
}

Ok(self)
} else {
Err("Maximum BigInt size exceeded".into())
}
}

/// Floored integer modulo.
Expand Down Expand Up @@ -55,48 +86,6 @@ impl_bigint_operator!(BitAnd, bitand, BitAndAssign, bitand_assign);
impl_bigint_operator!(BitOr, bitor, BitOrAssign, bitor_assign);
impl_bigint_operator!(BitXor, bitxor, BitXorAssign, bitxor_assign);

impl std::ops::Shr for BigInt {
type Output = Self;

fn shr(mut self, other: Self) -> Self::Output {
use std::ops::ShlAssign;
use std::ops::ShrAssign;

if let Some(n) = other.0.to_i32() {
if n > 0 {
self.0.shr_assign(n as usize)
} else {
self.0.shl_assign(n.abs() as usize)
}

return self;
}

panic!("RangeError: Maximum BigInt size exceeded");
}
}

impl std::ops::Shl for BigInt {
type Output = Self;

fn shl(mut self, other: Self) -> Self::Output {
use std::ops::ShlAssign;
use std::ops::ShrAssign;

if let Some(n) = other.0.to_i32() {
if n > 0 {
self.0.shl_assign(n as usize)
} else {
self.0.shr_assign(n.abs() as usize)
}

return self;
}

panic!("RangeError: Maximum BigInt size exceeded");
}
}

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

Expand Down
35 changes: 35 additions & 0 deletions boa/src/builtins/bigint/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,41 @@ fn pow() {
);
}

#[test]
fn pow_negative_exponent() {
let mut engine = Context::new();

assert_throws(&mut engine, "10n ** (-10n)", "RangeError");
}

#[test]
fn shl() {
let mut engine = Context::new();

assert_eq!(forward(&mut engine, "8n << 2n"), "32n");
}

#[test]
fn shl_out_of_range() {
let mut engine = Context::new();

assert_throws(&mut engine, "1000n << 1000000000000000n", "RangeError");
}

#[test]
fn shr() {
let mut engine = Context::new();

assert_eq!(forward(&mut engine, "8n >> 2n"), "2n");
}

#[test]
fn shr_out_of_range() {
let mut engine = Context::new();

assert_throws(&mut engine, "1000n >> 1000000000000000n", "RangeError");
}

#[test]
fn to_string() {
let mut engine = Context::new();
Expand Down
52 changes: 36 additions & 16 deletions boa/src/value/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,22 @@ impl Value {
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x).powf(*y)),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x.powi(*y)),

(Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(
a.as_inner()
.clone()
.pow(b)
.map_err(|msg| ctx.construct_range_error(msg))?,
),

// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone().pow(b))
}
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::bigint(
a.as_inner()
.clone()
.pow(b)
.map_err(|msg| ctx.construct_range_error(msg))?,
),
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
Expand Down Expand Up @@ -304,18 +312,24 @@ impl Value {
Self::integer(f64_to_int32(*x).wrapping_shl(*y as u32))
}

(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() << b.as_inner().clone())
}
(Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(
a.as_inner()
.clone()
.shift_left(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),

// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))
}
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
Self::bigint(x.as_inner().clone() << y.as_inner().clone())
}
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint(
x.as_inner()
.clone()
.shift_left(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
Expand All @@ -340,18 +354,24 @@ impl Value {
Self::integer(f64_to_int32(*x).wrapping_shr(*y as u32))
}

(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() >> b.as_inner().clone())
}
(Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(
a.as_inner()
.clone()
.shift_right(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),

// Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))
}
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
Self::bigint(x.as_inner().clone() >> y.as_inner().clone())
}
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint(
x.as_inner()
.clone()
.shift_right(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
Expand Down

0 comments on commit f2f2153

Please sign in to comment.