Browse Source

Fix some panics related to BigInt operations (#884)

* Fix some panics related to BigInt operations

* Address review comments

* Address review comments
pull/890/head
George Roman 4 years ago committed by GitHub
parent
commit
f2f2153a04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      boa/src/builtins/bigint/mod.rs
  2. 91
      boa/src/builtins/bigint/operations.rs
  3. 35
      boa/src/builtins/bigint/tests.rs
  4. 52
      boa/src/value/operations.rs

21
boa/src/builtins/bigint/mod.rs

@ -173,9 +173,17 @@ impl BigInt {
pub(crate) fn as_int_n(_this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { 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)?; 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( 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 { } else {
Ok(Value::from(modulo)) Ok(Value::from(modulo))
@ -214,10 +222,11 @@ impl BigInt {
let bigint = bigint_arg.to_bigint(ctx)?; let bigint = bigint_arg.to_bigint(ctx)?;
Ok(( Ok((
bigint bigint.as_inner().clone().mod_floor(
.as_inner() &BigInt::from(2)
.clone() .pow(&BigInt::from(bits as i64))
.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))), .expect("the exponent must be positive"),
),
bits, bits,
)) ))
} }

91
boa/src/builtins/bigint/operations.rs

@ -7,15 +7,46 @@ use super::BigInt;
impl BigInt { impl BigInt {
#[inline] #[inline]
pub fn pow(self, other: &Self) -> Self { pub fn pow(self, other: &Self) -> Result<Self, String> {
Self( Ok(Self(self.0.pow(
self.0.pow( other.0.to_biguint().ok_or("BigInt negative exponent")?,
other )))
.0 }
.to_biguint()
.expect("RangeError: \"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. /// Floored integer modulo.
@ -55,48 +86,6 @@ impl_bigint_operator!(BitAnd, bitand, BitAndAssign, bitand_assign);
impl_bigint_operator!(BitOr, bitor, BitOrAssign, bitor_assign); impl_bigint_operator!(BitOr, bitor, BitOrAssign, bitor_assign);
impl_bigint_operator!(BitXor, bitxor, BitXorAssign, bitxor_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 { impl std::ops::Neg for BigInt {
type Output = Self; type Output = Self;

35
boa/src/builtins/bigint/tests.rs

@ -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] #[test]
fn to_string() { fn to_string() {
let mut engine = Context::new(); let mut engine = Context::new();

52
boa/src/value/operations.rs

@ -176,14 +176,22 @@ impl Value {
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x).powf(*y)), (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::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: // Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)), (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)),
(Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::bigint(
Self::bigint(a.as_inner().clone().pow(b)) a.as_inner()
} .clone()
.pow(b)
.map_err(|msg| ctx.construct_range_error(msg))?,
),
(_, _) => { (_, _) => {
return ctx.throw_type_error( return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions", "cannot mix BigInt and other types, use explicit conversions",
@ -304,18 +312,24 @@ impl Value {
Self::integer(f64_to_int32(*x).wrapping_shl(*y as u32)) Self::integer(f64_to_int32(*x).wrapping_shl(*y as u32))
} }
(Self::BigInt(ref a), Self::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(
Self::bigint(a.as_inner().clone() << b.as_inner().clone()) a.as_inner()
} .clone()
.shift_left(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
// Slow path: // Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(x), Numeric::Number(y)) => { (Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y))) Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))
} }
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint(
Self::bigint(x.as_inner().clone() << y.as_inner().clone()) x.as_inner()
} .clone()
.shift_left(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
(_, _) => { (_, _) => {
return ctx.throw_type_error( return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions", "cannot mix BigInt and other types, use explicit conversions",
@ -340,18 +354,24 @@ impl Value {
Self::integer(f64_to_int32(*x).wrapping_shr(*y as u32)) Self::integer(f64_to_int32(*x).wrapping_shr(*y as u32))
} }
(Self::BigInt(ref a), Self::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(
Self::bigint(a.as_inner().clone() >> b.as_inner().clone()) a.as_inner()
} .clone()
.shift_right(b.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
// Slow path: // Slow path:
(_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) {
(Numeric::Number(x), Numeric::Number(y)) => { (Numeric::Number(x), Numeric::Number(y)) => {
Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y))) Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))
} }
(Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::bigint(
Self::bigint(x.as_inner().clone() >> y.as_inner().clone()) x.as_inner()
} .clone()
.shift_right(y.as_inner().clone())
.map_err(|msg| ctx.construct_range_error(&msg))?,
),
(_, _) => { (_, _) => {
return ctx.throw_type_error( return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions", "cannot mix BigInt and other types, use explicit conversions",

Loading…
Cancel
Save