Browse Source

Fix all `Value` operations and add unsigned shift right (#520)

pull/538/head
HalidOdat 4 years ago committed by GitHub
parent
commit
357c7d07f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      boa/src/builtins/array/mod.rs
  2. 8
      boa/src/builtins/boolean/tests.rs
  3. 86
      boa/src/builtins/number/conversions.rs
  4. 65
      boa/src/builtins/number/mod.rs
  5. 2
      boa/src/builtins/value/conversions.rs
  6. 2
      boa/src/builtins/value/display.rs
  7. 33
      boa/src/builtins/value/mod.rs
  8. 432
      boa/src/builtins/value/operations.rs
  9. 140
      boa/src/builtins/value/tests.rs
  10. 2
      boa/src/exec/conditional/mod.rs
  11. 6
      boa/src/exec/iteration/mod.rs
  12. 67
      boa/src/exec/mod.rs
  13. 93
      boa/src/exec/operator/mod.rs
  14. 183
      boa/src/exec/tests.rs

14
boa/src/builtins/array/mod.rs

@ -514,8 +514,8 @@ impl Array {
while i < len {
let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?.is_true();
if !result {
let result = interpreter.call(callback, &this_arg, &arguments)?;
if !result.to_boolean() {
return Ok(Value::from(false));
}
len = min(max_len, i32::from(&this.get_field("length")));
@ -695,7 +695,7 @@ impl Array {
let element = this.get_field(i.to_string());
let arguments = [element.clone(), Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?;
if result.is_true() {
if result.to_boolean() {
return Ok(element);
}
}
@ -737,7 +737,7 @@ impl Array {
let result = interpreter.call(predicate_arg, &this_arg, &arguments)?;
if result.is_true() {
if result.to_boolean() {
return Ok(Value::rational(f64::from(i)));
}
}
@ -902,7 +902,7 @@ impl Array {
.call(&callback, &this_val, &args)
.unwrap_or_else(|_| Value::undefined());
if callback_result.is_true() {
if callback_result.to_boolean() {
Some(element)
} else {
None
@ -946,8 +946,8 @@ impl Array {
while i < len {
let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(callback, &this_arg, &arguments)?.is_true();
if result {
let result = interpreter.call(callback, &this_arg, &arguments)?;
if result.to_boolean() {
return Ok(Value::from(true));
}
// the length of the array must be updated because the callback can mutate it.

8
boa/src/builtins/boolean/tests.rs

@ -50,10 +50,10 @@ fn constructor_gives_true_instance() {
assert_eq!(true_bool.is_object(), true);
// Values should all be truthy
assert_eq!(true_val.is_true(), true);
assert_eq!(true_num.is_true(), true);
assert_eq!(true_string.is_true(), true);
assert_eq!(true_bool.is_true(), true);
assert_eq!(true_val.to_boolean(), true);
assert_eq!(true_num.to_boolean(), true);
assert_eq!(true_string.to_boolean(), true);
assert_eq!(true_bool.to_boolean(), true);
}
#[test]

86
boa/src/builtins/number/conversions.rs

@ -0,0 +1,86 @@
/// Converts a 64-bit floating point number to an `i32` according to the [`ToInt32`][ToInt32] algorithm.
///
/// [ToInt32]: https://tc39.es/ecma262/#sec-toint32
#[inline]
#[allow(clippy::float_cmp)]
pub(crate) fn f64_to_int32(number: f64) -> i32 {
const SIGN_MASK: u64 = 0x8000000000000000;
const EXPONENT_MASK: u64 = 0x7FF0000000000000;
const SIGNIFICAND_MASK: u64 = 0x000FFFFFFFFFFFFF;
const HIDDEN_BIT: u64 = 0x0010000000000000;
const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.
const SIGNIFICAND_SIZE: i32 = 53;
const EXPONENT_BIAS: i32 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE;
const DENORMAL_EXPONENT: i32 = -EXPONENT_BIAS + 1;
#[inline]
fn is_denormal(number: f64) -> bool {
(number.to_bits() & EXPONENT_MASK) == 0
}
#[inline]
fn exponent(number: f64) -> i32 {
if is_denormal(number) {
return DENORMAL_EXPONENT;
}
let d64 = number.to_bits();
let biased_e = ((d64 & EXPONENT_MASK) >> PHYSICAL_SIGNIFICAND_SIZE) as i32;
biased_e - EXPONENT_BIAS
}
#[inline]
fn significand(number: f64) -> u64 {
let d64 = number.to_bits();
let significand = d64 & SIGNIFICAND_MASK;
if !is_denormal(number) {
significand + HIDDEN_BIT
} else {
significand
}
}
#[inline]
fn sign(number: f64) -> i64 {
if (number.to_bits() & SIGN_MASK) == 0 {
1
} else {
-1
}
}
if number.is_finite() && number <= f64::from(i32::MAX) && number >= f64::from(i32::MIN) {
let i = number as i32;
if f64::from(i) == number {
return i;
}
}
let exponent = exponent(number);
let bits = if exponent < 0 {
if exponent <= -SIGNIFICAND_SIZE {
return 0;
}
significand(number) >> -exponent
} else {
if exponent > 31 {
return 0;
}
(significand(number) << exponent) & 0xFFFFFFFF
};
(sign(number) * (bits as i64)) as i32
}
/// Converts a 64-bit floating point number to an `u32` according to the [`ToUint32`][ToUint32] algorithm.
///
// [ToInt32]: https://tc39.es/ecma262/#sec-touint32
#[inline]
pub(crate) fn f64_to_uint32(number: f64) -> u32 {
f64_to_int32(number) as u32
}

65
boa/src/builtins/number/mod.rs

@ -13,9 +13,6 @@
//! [spec]: https://tc39.es/ecma262/#sec-number-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
#[cfg(test)]
mod tests;
use super::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
@ -27,6 +24,13 @@ use crate::{
};
use num_traits::float::FloatCore;
mod conversions;
pub(crate) use conversions::{f64_to_int32, f64_to_uint32};
#[cfg(test)]
mod tests;
const BUF_SIZE: usize = 2200;
/// `Number` implementation.
@ -46,6 +50,52 @@ impl Number {
/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;
/// The `Number.MAX_SAFE_INTEGER` constant represents the maximum safe integer in JavaScript (`2^53 - 1`).
///
/// /// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-number.max_safe_integer
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
pub(crate) const MAX_SAFE_INTEGER: f64 = 9_007_199_254_740_991_f64;
/// The `Number.MIN_SAFE_INTEGER` constant represents the minimum safe integer in JavaScript (`-(253 - 1)`).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-number.min_safe_integer
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER
pub(crate) const MIN_SAFE_INTEGER: f64 = -9_007_199_254_740_991_f64;
/// The `Number.MAX_VALUE` property represents the maximum numeric value representable in JavaScript.
///
/// The `MAX_VALUE` property has a value of approximately `1.79E+308`, or `2^1024`.
/// Values larger than `MAX_VALUE` are represented as `Infinity`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-number.max_value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE
pub(crate) const MAX_VALUE: f64 = f64::MAX;
/// The `Number.MIN_VALUE` property represents the smallest positive numeric value representable in JavaScript.
///
/// The `MIN_VALUE` property is the number closest to `0`, not the most negative number, that JavaScript can represent.
/// It has a value of approximately `5e-324`. Values smaller than `MIN_VALUE` ("underflow values") are converted to `0`.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-number.min_value
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE
pub(crate) const MIN_VALUE: f64 = f64::MIN;
/// This function returns a `Result` of the number `Value`.
///
/// If the `Value` is a `Number` primitive of `Number` object the number is returned.
@ -53,7 +103,6 @@ impl Number {
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue
fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result<f64, Value> {
@ -539,10 +588,10 @@ impl Number {
{
let mut properties = number.as_object_mut().expect("'Number' object");
properties.insert_field("EPSILON", Value::from(f64::EPSILON));
properties.insert_field("MAX_SAFE_INTEGER", Value::from(9_007_199_254_740_991_f64));
properties.insert_field("MIN_SAFE_INTEGER", Value::from(-9_007_199_254_740_991_f64));
properties.insert_field("MAX_VALUE", Value::from(f64::MAX));
properties.insert_field("MIN_VALUE", Value::from(f64::MIN));
properties.insert_field("MAX_SAFE_INTEGER", Value::from(Self::MAX_SAFE_INTEGER));
properties.insert_field("MIN_SAFE_INTEGER", Value::from(Self::MIN_SAFE_INTEGER));
properties.insert_field("MAX_VALUE", Value::from(Self::MAX_VALUE));
properties.insert_field("MIN_VALUE", Value::from(Self::MIN_VALUE));
properties.insert_field("NEGATIVE_INFINITY", Value::from(f64::NEG_INFINITY));
properties.insert_field("POSITIVE_INFINITY", Value::from(f64::INFINITY));
properties.insert_field("NaN", Value::from(f64::NAN));

2
boa/src/builtins/value/conversions.rs

@ -120,7 +120,7 @@ impl From<bool> for Value {
impl From<&Value> for bool {
fn from(value: &Value) -> Self {
value.is_true()
value.to_boolean()
}
}

2
boa/src/builtins/value/display.rs

@ -131,7 +131,7 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String {
) -> String {
if let Value::Object(ref v) = *data {
// The in-memory address of the current object
let addr = address_of(v.borrow().deref());
let addr = address_of(v.as_ref());
// We need not continue if this object has already been
// printed up the current chain

33
boa/src/builtins/value/mod.rs

@ -28,7 +28,6 @@ use std::{
convert::TryFrom,
f64::NAN,
fmt::{self, Display},
ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub},
str::FromStr,
};
@ -159,14 +158,6 @@ impl Value {
Self::Symbol(RcSymbol::from(symbol))
}
/// Helper function to convert the `Value` to a number and compute its power.
pub fn as_num_to_power(&self, other: Self) -> Self {
match (self, other) {
(Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)),
(a, b) => Self::rational(a.to_number().powf(b.to_number())),
}
}
/// Returns a new empty object
pub fn new_object(global: Option<&Value>) -> Self {
let _timer = BoaProfiler::global().start_event("new_object", "value");
@ -413,21 +404,6 @@ impl Value {
}
}
/// Returns true if the value is true.
///
/// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
pub fn is_true(&self) -> bool {
match *self {
Self::Object(_) => true,
Self::String(ref s) if !s.is_empty() => true,
Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
Self::Integer(n) if n != 0 => true,
Self::Boolean(v) => v,
Self::BigInt(ref n) if *n.as_inner() != 0 => true,
_ => false,
}
}
/// Converts the value into a 64-bit floating point number
pub fn to_number(&self) -> f64 {
match *self {
@ -473,7 +449,12 @@ impl Value {
}
}
/// Creates a new boolean value from the input
/// Converts the value to a `bool` type.
///
/// More information:
/// - [ECMAScript][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-toboolean
pub fn to_boolean(&self) -> bool {
match *self {
Self::Undefined | Self::Null => false,
@ -713,7 +694,7 @@ impl Value {
#[inline]
pub fn set_data(&self, data: ObjectData) {
if let Self::Object(ref obj) = *self {
(*obj.deref().borrow_mut()).data = data;
obj.borrow_mut().data = data;
}
}

432
boa/src/builtins/value/operations.rs

@ -1,132 +1,389 @@
use super::*;
use crate::builtins::number::{f64_to_int32, f64_to_uint32};
use crate::exec::PreferredType;
impl Add for Value {
type Output = Self;
fn add(self, other: Self) -> Self {
match (self, other) {
(Self::String(ref s), ref o) => {
Self::string(format!("{}{}", s.clone(), &o.to_string()))
}
impl Value {
#[inline]
pub fn add(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) + f64::from(*y)),
(Self::Rational(x), Self::Rational(y)) => Self::rational(x + y),
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) + y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)),
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)),
(Self::BigInt(ref n1), Self::BigInt(ref n2)) => {
Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
}
(ref s, Self::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)),
(ref s, ref o) => Self::rational(s.to_number() + o.to_number()),
}
// Slow path:
(_, _) => match (
ctx.to_primitive(self, PreferredType::Default)?,
ctx.to_primitive(other, PreferredType::Default)?,
) {
(Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)),
(ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)),
(x, y) => match (ctx.to_numeric(&x)?, ctx.to_numeric(&y)?) {
(Self::Rational(x), Self::Rational(y)) => Self::rational(x + y),
(Self::BigInt(ref n1), Self::BigInt(ref n2)) => {
Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
)
}
},
},
})
}
}
impl Sub for Value {
type Output = Self;
fn sub(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn sub(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) - f64::from(*y)),
(Self::Rational(x), Self::Rational(y)) => Self::rational(x - y),
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) - y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x - f64::from(*y)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() - b.as_inner().clone())
}
(a, b) => Self::rational(a.to_number() - b.to_number()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => Self::rational(a - b),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() - b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Mul for Value {
type Output = Self;
fn mul(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn mul(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) * f64::from(*y)),
(Self::Rational(x), Self::Rational(y)) => Self::rational(x * y),
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) * y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x * f64::from(*y)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() * b.as_inner().clone())
}
(a, b) => Self::rational(a.to_number() * b.to_number()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => Self::rational(a * b),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() * b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Div for Value {
type Output = Self;
fn div(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn div(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x) / f64::from(*y)),
(Self::Rational(x), Self::Rational(y)) => Self::rational(x / y),
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) / y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x / f64::from(*y)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() / b.as_inner().clone())
}
(a, b) => Self::rational(a.to_number() / b.to_number()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => Self::rational(a / b),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() / b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Rem for Value {
type Output = Self;
fn rem(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn rem(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x % *y),
(Self::Rational(x), Self::Rational(y)) => Self::rational(x % y),
(Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) % y),
(Self::Rational(x), Self::Integer(y)) => Self::rational(x % f64::from(*y)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() % b.as_inner().clone())
}
(a, b) => Self::rational(a.to_number() % b.to_number()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => Self::rational(a % b),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() % b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl BitAnd for Value {
type Output = Self;
fn bitand(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn pow(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::rational(f64::from(*x).powi(*y)),
(Self::Rational(x), Self::Rational(y)) => Self::rational(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::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)),
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => Self::rational(a.powf(b)),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone().pow(b))
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
#[inline]
pub fn bitand(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x & y),
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(*x) & f64_to_int32(*y))
}
(Self::Integer(x), Self::Rational(y)) => Self::integer(x & f64_to_int32(*y)),
(Self::Rational(x), Self::Integer(y)) => Self::integer(f64_to_int32(*x) & y),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() & b.as_inner().clone())
}
(a, b) => Self::integer(a.to_integer() & b.to_integer()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => {
Self::integer(f64_to_int32(a) & f64_to_int32(b))
}
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() & b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl BitOr for Value {
type Output = Self;
fn bitor(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn bitor(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x | y),
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(*x) | f64_to_int32(*y))
}
(Self::Integer(x), Self::Rational(y)) => Self::integer(x | f64_to_int32(*y)),
(Self::Rational(x), Self::Integer(y)) => Self::integer(f64_to_int32(*x) | y),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() | b.as_inner().clone())
}
(a, b) => Self::integer(a.to_integer() | b.to_integer()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => {
Self::integer(f64_to_int32(a) | f64_to_int32(b))
}
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() | b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl BitXor for Value {
type Output = Self;
fn bitxor(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn bitxor(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x ^ y),
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(*x) ^ f64_to_int32(*y))
}
(Self::Integer(x), Self::Rational(y)) => Self::integer(x ^ f64_to_int32(*y)),
(Self::Rational(x), Self::Integer(y)) => Self::integer(f64_to_int32(*x) ^ y),
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() ^ b.as_inner().clone())
}
(a, b) => Self::integer(a.to_integer() ^ b.to_integer()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(a), Self::Rational(b)) => {
Self::integer(f64_to_int32(a) ^ f64_to_int32(b))
}
(Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.as_inner().clone() ^ b.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Shl for Value {
type Output = Self;
fn shl(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn shl(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shl(*y as u32)),
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(*x).wrapping_shl(f64_to_uint32(*y)))
}
(Self::Integer(x), Self::Rational(y)) => {
Self::integer(x.wrapping_shl(f64_to_uint32(*y)))
}
(Self::Rational(x), Self::Integer(y)) => {
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())
}
(a, b) => Self::integer(a.to_integer() << b.to_integer()),
}
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))
}
(Self::BigInt(ref x), Self::BigInt(ref y)) => {
Self::bigint(x.as_inner().clone() << y.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Shr for Value {
type Output = Self;
fn shr(self, other: Self) -> Self {
match (self, other) {
#[inline]
pub fn shr(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => Self::integer(x.wrapping_shr(*y as u32)),
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(*x).wrapping_shr(f64_to_uint32(*y)))
}
(Self::Integer(x), Self::Rational(y)) => {
Self::integer(x.wrapping_shr(f64_to_uint32(*y)))
}
(Self::Rational(x), Self::Integer(y)) => {
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())
}
(a, b) => Self::integer(a.to_integer() >> b.to_integer()),
}
}
}
impl Not for Value {
type Output = Self;
fn not(self) -> Self {
Self::boolean(!self.is_true())
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(x), Self::Rational(y)) => {
Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))
}
(Self::BigInt(ref x), Self::BigInt(ref y)) => {
Self::bigint(x.as_inner().clone() >> y.as_inner().clone())
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
}
impl Neg for Value {
type Output = Self;
#[inline]
pub fn ushr(&self, other: &Self, ctx: &mut Interpreter) -> ResultValue {
Ok(match (self, other) {
// Fast path:
(Self::Integer(x), Self::Integer(y)) => {
Self::rational((*x as u32).wrapping_shr(*y as u32))
}
(Self::Rational(x), Self::Rational(y)) => {
Self::rational(f64_to_uint32(*x).wrapping_shr(f64_to_uint32(*y)))
}
(Self::Integer(x), Self::Rational(y)) => {
Self::rational((*x as u32).wrapping_shr(f64_to_uint32(*y)))
}
(Self::Rational(x), Self::Integer(y)) => {
Self::rational(f64_to_uint32(*x).wrapping_shr(*y as u32))
}
fn neg(self) -> Self::Output {
match self {
// Slow path:
(_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) {
(Self::Rational(x), Self::Rational(y)) => {
Self::rational(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y)))
}
(Self::BigInt(_), Self::BigInt(_)) => {
return ctx
.throw_type_error("BigInts have no unsigned right shift, use >> instead");
}
(_, _) => {
return ctx.throw_type_error(
"cannot mix BigInt and other types, use explicit conversions",
);
}
},
})
}
#[inline]
pub fn neg(&self, _: &mut Interpreter) -> ResultValue {
Ok(match *self {
Self::Object(_) | Self::Symbol(_) | Self::Undefined => Self::rational(NAN),
Self::String(ref str) => Self::rational(match f64::from_str(str) {
Ok(num) => -num,
@ -137,6 +394,11 @@ impl Neg for Value {
Self::Boolean(true) => Self::integer(1),
Self::Boolean(false) | Self::Null => Self::integer(0),
Self::BigInt(ref num) => Self::bigint(-num.as_inner().clone()),
}
})
}
#[inline]
pub fn not(&self, _: &mut Interpreter) -> ResultValue {
Ok(Self::boolean(!self.to_boolean()))
}
}

140
boa/src/builtins/value/tests.rs

@ -5,13 +5,13 @@ use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[test]
fn check_is_object() {
fn is_object() {
let val = Value::new_object(None);
assert_eq!(val.is_object(), true);
}
#[test]
fn check_string_to_value() {
fn string_to_value() {
let s = String::from("Hello");
let v = Value::from(s);
assert_eq!(v.is_string(), true);
@ -19,14 +19,14 @@ fn check_string_to_value() {
}
#[test]
fn check_undefined() {
fn undefined() {
let u = Value::Undefined;
assert_eq!(u.get_type(), Type::Undefined);
assert_eq!(u.to_string(), "undefined");
}
#[test]
fn check_get_set_field() {
fn get_set_field() {
let obj = Value::new_object(None);
// Create string and convert it to a Value
let s = Value::from("bar");
@ -35,20 +35,20 @@ fn check_get_set_field() {
}
#[test]
fn check_integer_is_true() {
assert_eq!(Value::from(1).is_true(), true);
assert_eq!(Value::from(0).is_true(), false);
assert_eq!(Value::from(-1).is_true(), true);
fn integer_is_true() {
assert_eq!(Value::from(1).to_boolean(), true);
assert_eq!(Value::from(0).to_boolean(), false);
assert_eq!(Value::from(-1).to_boolean(), true);
}
#[test]
fn check_number_is_true() {
assert_eq!(Value::from(1.0).is_true(), true);
assert_eq!(Value::from(0.1).is_true(), true);
assert_eq!(Value::from(0.0).is_true(), false);
assert_eq!(Value::from(-0.0).is_true(), false);
assert_eq!(Value::from(-1.0).is_true(), true);
assert_eq!(Value::from(NAN).is_true(), false);
fn number_is_true() {
assert_eq!(Value::from(1.0).to_boolean(), true);
assert_eq!(Value::from(0.1).to_boolean(), true);
assert_eq!(Value::from(0.0).to_boolean(), false);
assert_eq!(Value::from(-0.0).to_boolean(), false);
assert_eq!(Value::from(-1.0).to_boolean(), true);
assert_eq!(Value::from(NAN).to_boolean(), false);
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
@ -213,3 +213,113 @@ fn get_types() {
Type::Symbol
);
}
#[test]
fn add_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "1 + 2").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 3);
}
#[test]
fn add_number_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "1 + \" + 2 = 3\"").unwrap();
let value = engine.to_string(&value).unwrap();
assert_eq!(value, "1 + 2 = 3");
}
#[test]
fn add_string_and_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "\"Hello\" + \", world\"").unwrap();
let value = engine.to_string(&value).unwrap();
assert_eq!(value, "Hello, world");
}
#[test]
fn add_number_object_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "new Number(10) + 6").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 16);
}
#[test]
fn add_number_object_and_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "new Number(10) + new String(\"0\")").unwrap();
let value = engine.to_string(&value).unwrap();
assert_eq!(value, "100");
}
#[test]
fn sub_number_and_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "1 - 999").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, -998);
}
#[test]
fn sub_number_object_and_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "new Number(1) - new Number(999)").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, -998);
}
#[test]
fn sub_string_and_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "'Hello' - new Number(999)").unwrap();
let value = engine.to_number(&value).unwrap();
assert!(value.is_nan());
}
#[test]
fn bitand_integer_and_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "0xFFFF & 0xFF").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 255);
}
#[test]
fn bitand_integer_and_rational() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "0xFFFF & 255.5").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 255);
}
#[test]
fn bitand_rational_and_rational() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let value = forward_val(&mut engine, "255.772 & 255.5").unwrap();
let value = engine.to_int32(&value).unwrap();
assert_eq!(value, 255);
}

2
boa/src/exec/conditional/mod.rs

@ -7,7 +7,7 @@ use std::borrow::Borrow;
impl Executable for If {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
Ok(if self.cond().run(interpreter)?.borrow().is_true() {
Ok(if self.cond().run(interpreter)?.borrow().to_boolean() {
self.body().run(interpreter)?
} else if let Some(ref else_e) = self.else_node() {
else_e.run(interpreter)?

6
boa/src/exec/iteration/mod.rs

@ -29,7 +29,7 @@ impl Executable for ForLoop {
while self
.condition()
.map(|cond| cond.run(interpreter).map(|v| v.is_true()))
.map(|cond| cond.run(interpreter).map(|v| v.to_boolean()))
.transpose()?
.unwrap_or(true)
{
@ -66,7 +66,7 @@ impl Executable for ForLoop {
impl Executable for WhileLoop {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let mut result = Value::undefined();
while self.cond().run(interpreter)?.borrow().is_true() {
while self.cond().run(interpreter)?.borrow().to_boolean() {
result = self.expr().run(interpreter)?;
match interpreter.get_current_state() {
InterpreterState::Break(_label) => {
@ -107,7 +107,7 @@ impl Executable for DoWhileLoop {
}
}
while self.cond().run(interpreter)?.borrow().is_true() {
while self.cond().run(interpreter)?.borrow().to_boolean() {
result = self.body().run(interpreter)?;
match interpreter.get_current_state() {
InterpreterState::Break(_label) => {

67
boa/src/exec/mod.rs

@ -24,6 +24,7 @@ mod try_node;
use crate::{
builtins::{
function::{Function as FunctionObject, FunctionBody, ThisMode},
number::{f64_to_int32, f64_to_uint32},
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{RcBigInt, RcString, ResultValue, Type, Value},
@ -231,29 +232,83 @@ impl Interpreter {
let integer_index = self.to_integer(value)?;
if integer_index < 0 {
if integer_index < 0.0 {
return Err(self.construct_range_error("Integer index must be >= 0"));
}
if integer_index > 2i64.pow(53) - 1 {
if integer_index > Number::MAX_SAFE_INTEGER {
return Err(self.construct_range_error("Integer index must be less than 2**(53) - 1"));
}
Ok(integer_index as usize)
}
/// Converts a value to an integral 64 bit signed integer.
/// Converts a value to an integral Number value.
///
/// See: https://tc39.es/ecma262/#sec-tointeger
#[allow(clippy::wrong_self_convention)]
pub fn to_integer(&mut self, value: &Value) -> Result<i64, Value> {
pub fn to_integer(&mut self, value: &Value) -> Result<f64, Value> {
// 1. Let number be ? ToNumber(argument).
let number = self.to_number(value)?;
if number.is_nan() {
// 2. If number is +∞ or -∞, return number.
if !number.is_finite() {
// 3. If number is NaN, +0, or -0, return +0.
if number.is_nan() {
return Ok(0.0);
}
return Ok(number);
}
// 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)).
// 5. If integer is -0, return +0.
// 6. Return integer.
Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0
}
/// Converts a value to an integral 32 bit signed integer.
///
/// See: https://tc39.es/ecma262/#sec-toint32
#[allow(clippy::wrong_self_convention)]
pub fn to_int32(&mut self, value: &Value) -> Result<i32, Value> {
// This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *value {
return Ok(number);
}
let number = self.to_number(value)?;
Ok(f64_to_int32(number))
}
/// Converts a value to an integral 32 bit unsigned integer.
///
/// See: https://tc39.es/ecma262/#sec-toint32
#[allow(clippy::wrong_self_convention)]
pub fn to_uint32(&mut self, value: &Value) -> Result<u32, Value> {
// This is the fast path, if the value is Integer we can just return it.
if let Value::Integer(number) = *value {
return Ok(number as u32);
}
let number = self.to_number(value)?;
Ok(f64_to_uint32(number))
}
/// Converts argument to an integer suitable for use as the length of an array-like object.
///
/// See: https://tc39.es/ecma262/#sec-tolength
#[allow(clippy::wrong_self_convention)]
pub fn to_length(&mut self, value: &Value) -> Result<usize, Value> {
// 1. Let len be ? ToInteger(argument).
let len = self.to_integer(value)?;
// 2. If len ≤ +0, return +0.
if len < 0.0 {
return Ok(0);
}
Ok(number as i64)
// 3. Return min(len, 2^53 - 1).
Ok(len.min(Number::MAX_SAFE_INTEGER) as usize)
}
/// Converts a value to a double precision floating point.

93
boa/src/exec/operator/mod.rs

@ -52,29 +52,28 @@ impl Executable for BinOp {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
match self.op() {
op::BinOp::Num(op) => {
let v_a = self.lhs().run(interpreter)?;
let v_b = self.rhs().run(interpreter)?;
Ok(match op {
NumOp::Add => v_a + v_b,
NumOp::Sub => v_a - v_b,
NumOp::Mul => v_a * v_b,
NumOp::Exp => v_a.as_num_to_power(v_b),
NumOp::Div => v_a / v_b,
NumOp::Mod => v_a % v_b,
})
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
match op {
NumOp::Add => x.add(&y, interpreter),
NumOp::Sub => x.sub(&y, interpreter),
NumOp::Mul => x.mul(&y, interpreter),
NumOp::Exp => x.pow(&y, interpreter),
NumOp::Div => x.div(&y, interpreter),
NumOp::Mod => x.rem(&y, interpreter),
}
}
op::BinOp::Bit(op) => {
let v_a = self.lhs().run(interpreter)?;
let v_b = self.rhs().run(interpreter)?;
Ok(match op {
BitOp::And => v_a & v_b,
BitOp::Or => v_a | v_b,
BitOp::Xor => v_a ^ v_b,
BitOp::Shl => v_a << v_b,
BitOp::Shr => v_a >> v_b,
// TODO Fix
BitOp::UShr => v_a >> v_b,
})
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
match op {
BitOp::And => x.bitand(&y, interpreter),
BitOp::Or => x.bitor(&y, interpreter),
BitOp::Xor => x.bitxor(&y, interpreter),
BitOp::Shl => x.shl(&y, interpreter),
BitOp::Shr => x.shr(&y, interpreter),
BitOp::UShr => x.ushr(&y, interpreter),
}
}
op::BinOp::Comp(op) => {
let v_a = self.lhs().run(interpreter)?;
@ -122,7 +121,7 @@ impl Executable for BinOp {
.get_binding_value(name.as_ref())
.ok_or_else(|| interpreter.construct_reference_error(name.as_ref()))?;
let v_b = self.rhs().run(interpreter)?;
let value = Self::run_assign(op, v_a, v_b);
let value = Self::run_assign(op, v_a, v_b, interpreter)?;
interpreter.realm.environment.set_mutable_binding(
name.as_ref(),
value.clone(),
@ -134,7 +133,7 @@ impl Executable for BinOp {
let v_r_a = get_const_field.obj().run(interpreter)?;
let v_a = v_r_a.get_field(get_const_field.field());
let v_b = self.rhs().run(interpreter)?;
let value = Self::run_assign(op, v_a, v_b);
let value = Self::run_assign(op, v_a, v_b, interpreter)?;
v_r_a.set_field(get_const_field.field(), value.clone());
Ok(value)
}
@ -146,49 +145,49 @@ impl Executable for BinOp {
impl BinOp {
/// Runs the assignment operators.
fn run_assign(op: AssignOp, v_a: Value, v_b: Value) -> Value {
fn run_assign(op: AssignOp, x: Value, y: Value, interpreter: &mut Interpreter) -> ResultValue {
match op {
AssignOp::Add => v_a + v_b,
AssignOp::Sub => v_a - v_b,
AssignOp::Mul => v_a * v_b,
AssignOp::Exp => v_a.as_num_to_power(v_b),
AssignOp::Div => v_a / v_b,
AssignOp::Mod => v_a % v_b,
AssignOp::And => v_a & v_b,
AssignOp::Or => v_a | v_b,
AssignOp::Xor => v_a ^ v_b,
AssignOp::Shl => v_a << v_b,
AssignOp::Shr => v_a << v_b,
AssignOp::Add => x.add(&y, interpreter),
AssignOp::Sub => x.sub(&y, interpreter),
AssignOp::Mul => x.mul(&y, interpreter),
AssignOp::Exp => x.pow(&y, interpreter),
AssignOp::Div => x.div(&y, interpreter),
AssignOp::Mod => x.rem(&y, interpreter),
AssignOp::And => x.bitand(&y, interpreter),
AssignOp::Or => x.bitor(&y, interpreter),
AssignOp::Xor => x.bitxor(&y, interpreter),
AssignOp::Shl => x.shl(&y, interpreter),
AssignOp::Shr => x.shr(&y, interpreter),
}
}
}
impl Executable for UnaryOp {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let v_a = self.target().run(interpreter)?;
let x = self.target().run(interpreter)?;
Ok(match self.op() {
op::UnaryOp::Minus => -v_a,
op::UnaryOp::Plus => Value::from(v_a.to_number()),
op::UnaryOp::Minus => x.neg(interpreter)?,
op::UnaryOp::Plus => Value::from(x.to_number()),
op::UnaryOp::IncrementPost => {
let ret = v_a.clone();
interpreter.set_value(self.target(), Value::from(v_a.to_number() + 1.0))?;
let ret = x.clone();
interpreter.set_value(self.target(), Value::from(x.to_number() + 1.0))?;
ret
}
op::UnaryOp::IncrementPre => {
interpreter.set_value(self.target(), Value::from(v_a.to_number() + 1.0))?
interpreter.set_value(self.target(), Value::from(x.to_number() + 1.0))?
}
op::UnaryOp::DecrementPost => {
let ret = v_a.clone();
interpreter.set_value(self.target(), Value::from(v_a.to_number() - 1.0))?;
let ret = x.clone();
interpreter.set_value(self.target(), Value::from(x.to_number() - 1.0))?;
ret
}
op::UnaryOp::DecrementPre => {
interpreter.set_value(self.target(), Value::from(v_a.to_number() - 1.0))?
interpreter.set_value(self.target(), Value::from(x.to_number() - 1.0))?
}
op::UnaryOp::Not => !v_a,
op::UnaryOp::Not => x.not(interpreter)?,
op::UnaryOp::Tilde => {
let num_v_a = v_a.to_number();
let num_v_a = x.to_number();
// NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184
Value::from(if num_v_a.is_nan() {
-1
@ -221,7 +220,7 @@ impl Executable for UnaryOp {
| Node::UnaryOp(_) => Value::boolean(true),
_ => panic!("SyntaxError: wrong delete argument {}", self),
},
op::UnaryOp::TypeOf => Value::from(v_a.get_type().as_str()),
op::UnaryOp::TypeOf => Value::from(x.get_type().as_str()),
})
}
}

183
boa/src/exec/tests.rs

@ -1,4 +1,10 @@
use crate::{builtins::Value, exec, exec::Interpreter, forward, realm::Realm};
use crate::{
builtins::{Number, Value},
exec,
exec::Interpreter,
forward,
realm::Realm,
};
#[test]
fn function_declaration_returns_undefined() {
@ -895,10 +901,177 @@ fn to_integer() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(engine.to_integer(&Value::number(f64::NAN)).unwrap(), 0);
assert_eq!(engine.to_integer(&Value::number(0.0f64)).unwrap(), 0);
assert_eq!(engine.to_integer(&Value::number(20.9)).unwrap(), 20);
assert_eq!(engine.to_integer(&Value::number(-20.9)).unwrap(), -20);
assert!(Number::equal(
engine.to_integer(&Value::number(f64::NAN)).unwrap(),
0.0
));
assert!(Number::equal(
engine
.to_integer(&Value::number(f64::NEG_INFINITY))
.unwrap(),
f64::NEG_INFINITY
));
assert!(Number::equal(
engine.to_integer(&Value::number(f64::INFINITY)).unwrap(),
f64::INFINITY
));
assert!(Number::equal(
engine.to_integer(&Value::number(0.0)).unwrap(),
0.0
));
let number = engine.to_integer(&Value::number(-0.0)).unwrap();
assert!(!number.is_sign_negative());
assert!(Number::equal(number, 0.0));
assert!(Number::equal(
engine.to_integer(&Value::number(20.9)).unwrap(),
20.0
));
assert!(Number::equal(
engine.to_integer(&Value::number(-20.9)).unwrap(),
-20.0
));
}
#[test]
fn to_length() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
assert_eq!(engine.to_length(&Value::number(f64::NAN)).unwrap(), 0);
assert_eq!(
engine.to_length(&Value::number(f64::NEG_INFINITY)).unwrap(),
0
);
assert_eq!(
engine.to_length(&Value::number(f64::INFINITY)).unwrap(),
Number::MAX_SAFE_INTEGER as usize
);
assert_eq!(engine.to_length(&Value::number(0.0)).unwrap(), 0);
assert_eq!(engine.to_length(&Value::number(-0.0)).unwrap(), 0);
assert_eq!(engine.to_length(&Value::number(20.9)).unwrap(), 20);
assert_eq!(engine.to_length(&Value::number(-20.9)).unwrap(), 0);
assert_eq!(
engine.to_length(&Value::number(100000000000.0)).unwrap(),
100000000000
);
assert_eq!(
engine.to_length(&Value::number(4010101101.0)).unwrap(),
4010101101
);
}
#[test]
fn to_int32() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
macro_rules! check_to_int32 {
($from:expr => $to:expr) => {
assert_eq!(engine.to_int32(&Value::number($from)).unwrap(), $to);
};
};
check_to_int32!(f64::NAN => 0);
check_to_int32!(f64::NEG_INFINITY => 0);
check_to_int32!(f64::INFINITY => 0);
check_to_int32!(0 => 0);
check_to_int32!(-0.0 => 0);
check_to_int32!(20.9 => 20);
check_to_int32!(-20.9 => -20);
check_to_int32!(Number::MIN_VALUE => 0);
check_to_int32!(-Number::MIN_VALUE => 0);
check_to_int32!(0.1 => 0);
check_to_int32!(-0.1 => 0);
check_to_int32!(1 => 1);
check_to_int32!(1.1 => 1);
check_to_int32!(-1 => -1);
check_to_int32!(0.6 => 0);
check_to_int32!(1.6 => 1);
check_to_int32!(-0.6 => 0);
check_to_int32!(-1.6 => -1);
check_to_int32!(2147483647.0 => 2147483647);
check_to_int32!(2147483648.0 => -2147483648);
check_to_int32!(2147483649.0 => -2147483647);
check_to_int32!(4294967295.0 => -1);
check_to_int32!(4294967296.0 => 0);
check_to_int32!(4294967297.0 => 1);
check_to_int32!(-2147483647.0 => -2147483647);
check_to_int32!(-2147483648.0 => -2147483648);
check_to_int32!(-2147483649.0 => 2147483647);
check_to_int32!(-4294967295.0 => 1);
check_to_int32!(-4294967296.0 => 0);
check_to_int32!(-4294967297.0 => -1);
check_to_int32!(2147483648.25 => -2147483648);
check_to_int32!(2147483648.5 => -2147483648);
check_to_int32!(2147483648.75 => -2147483648);
check_to_int32!(4294967295.25 => -1);
check_to_int32!(4294967295.5 => -1);
check_to_int32!(4294967295.75 => -1);
check_to_int32!(3000000000.25 => -1294967296);
check_to_int32!(3000000000.5 => -1294967296);
check_to_int32!(3000000000.75 => -1294967296);
check_to_int32!(-2147483648.25 => -2147483648);
check_to_int32!(-2147483648.5 => -2147483648);
check_to_int32!(-2147483648.75 => -2147483648);
check_to_int32!(-4294967295.25 => 1);
check_to_int32!(-4294967295.5 => 1);
check_to_int32!(-4294967295.75 => 1);
check_to_int32!(-3000000000.25 => 1294967296);
check_to_int32!(-3000000000.5 => 1294967296);
check_to_int32!(-3000000000.75 => 1294967296);
let base = 2f64.powf(64.0);
check_to_int32!(base + 0.0 => 0);
check_to_int32!(base + 1117.0 => 0);
check_to_int32!(base + 2234.0 => 4096);
check_to_int32!(base + 3351.0 => 4096);
check_to_int32!(base + 4468.0 => 4096);
check_to_int32!(base + 5585.0 => 4096);
check_to_int32!(base + 6702.0 => 8192);
check_to_int32!(base + 7819.0 => 8192);
check_to_int32!(base + 8936.0 => 8192);
check_to_int32!(base + 10053.0 => 8192);
check_to_int32!(base + 11170.0 => 12288);
check_to_int32!(base + 12287.0 => 12288);
check_to_int32!(base + 13404.0 => 12288);
check_to_int32!(base + 14521.0 => 16384);
check_to_int32!(base + 15638.0 => 16384);
check_to_int32!(base + 16755.0 => 16384);
check_to_int32!(base + 17872.0 => 16384);
check_to_int32!(base + 18989.0 => 20480);
check_to_int32!(base + 20106.0 => 20480);
check_to_int32!(base + 21223.0 => 20480);
check_to_int32!(base + 22340.0 => 20480);
check_to_int32!(base + 23457.0 => 24576);
check_to_int32!(base + 24574.0 => 24576);
check_to_int32!(base + 25691.0 => 24576);
check_to_int32!(base + 26808.0 => 28672);
check_to_int32!(base + 27925.0 => 28672);
check_to_int32!(base + 29042.0 => 28672);
check_to_int32!(base + 30159.0 => 28672);
check_to_int32!(base + 31276.0 => 32768);
// bignum is (2^53 - 1) * 2^31 - highest number with bit 31 set.
let bignum = 2f64.powf(84.0) - 2f64.powf(31.0);
check_to_int32!(bignum => -2147483648);
check_to_int32!(-bignum => -2147483648);
check_to_int32!(2.0 * bignum => 0);
check_to_int32!(-(2.0 * bignum) => 0);
check_to_int32!(bignum - 2f64.powf(31.0) => 0);
check_to_int32!(-(bignum - 2f64.powf(31.0)) => 0);
// max_fraction is largest number below 1.
let max_fraction = 1.0 - 2f64.powf(-53.0);
check_to_int32!(max_fraction => 0);
check_to_int32!(-max_fraction => 0);
}
#[test]

Loading…
Cancel
Save