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

Fix all Value operations and add unsigned shift right #520

Merged
merged 13 commits into from
Jul 2, 2020
Merged
14 changes: 7 additions & 7 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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)));
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions boa/src/builtins/boolean/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
86 changes: 86 additions & 0 deletions boa/src/builtins/number/conversions.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 57 additions & 8 deletions boa/src/builtins/number/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -46,14 +50,59 @@ 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.
/// Otherwise an `TypeError` is thrown.
///
/// 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> {
Expand Down Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/value/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/value/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 7 additions & 26 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
}

Expand Down
Loading