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

impl abstract-equality-comparison #395

Merged
merged 11 commits into from
May 13, 2020
21 changes: 15 additions & 6 deletions boa/src/builtins/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,10 @@ impl ValueData {

/// Returns true if the value is a number
pub fn is_number(&self) -> bool {
self.is_double()
match self {
Self::Rational(_) | Self::Integer(_) => true,
_ => false
}
}

/// Returns true if the value is a string
Expand Down Expand Up @@ -295,13 +298,19 @@ impl ValueData {
pub fn to_number(&self) -> f64 {
match *self {
Self::Object(_) | Self::Symbol(_) | Self::Undefined => NAN,
Self::String(ref str) => match FromStr::from_str(str) {
Ok(num) => num,
Err(_) => NAN,
},
Self::Rational(num) => num,
Self::String(ref str) => {
if str.is_empty() {
return 0.0;
}

match FromStr::from_str(str) {
Ok(num) => num,
Err(_) => NAN,
}
}
Self::Boolean(true) => 1.0,
Self::Boolean(false) | Self::Null => 0.0,
Self::Rational(num) => num,
Self::Integer(num) => f64::from(num),
}
}
Expand Down
115 changes: 115 additions & 0 deletions boa/src/builtins/value/operations.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,72 @@
use super::*;
use crate::Interpreter;
use std::borrow::Borrow;

#[allow(clippy::float_cmp)]
/// https://tc39.es/ecma262/#sec-numeric-types-number-equal
Razican marked this conversation as resolved.
Show resolved Hide resolved
fn strict_number_equals<T: Into<f64>>(a: T, b: T) -> bool {
let a: f64 = a.into();
let b: f64 = b.into();

if a.is_nan() || b.is_nan() {
return false;
}

a == b
Razican marked this conversation as resolved.
Show resolved Hide resolved
}

impl Value {
// https://tc39.es/ecma262/#sec-strict-equality-comparison
hello2dj marked this conversation as resolved.
Show resolved Hide resolved
pub fn strict_equals(&self, other: &Self) -> bool {
if self.get_type() != other.get_type() {
return false;
}

if self.is_number() {
return strict_number_equals(self, other)
}

same_value_non_number(self, other)
}

// https://tc39.es/ecma262/#sec-abstract-equality-comparison
hello2dj marked this conversation as resolved.
Show resolved Hide resolved
pub fn equals(&mut self, other: &mut Self, interpreter: &mut Interpreter) -> bool {
if self.get_type() == other.get_type() {
return self.strict_equals(other);
}

match (self.data(), other.data()) {
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true,

// https://github.com/rust-lang/rust/issues/54883
(ValueData::Integer(_), ValueData::String(_))
| (ValueData::Rational(_), ValueData::String(_))
| (ValueData::String(_), ValueData::Integer(_))
| (ValueData::String(_), ValueData::Rational(_))
| (ValueData::Rational(_), ValueData::Boolean(_))
| (ValueData::Integer(_), ValueData::Boolean(_)) => {
let a: &Value = self.borrow();
let b: &Value = other.borrow();
strict_number_equals(a, b)
}
(ValueData::Boolean(_), _) => {
other.equals(&mut Value::from(self.to_integer()), interpreter)
}
(_, ValueData::Boolean(_)) => {
self.equals(&mut Value::from(other.to_integer()), interpreter)
}
(ValueData::Object(_), _) => {
let mut primitive = interpreter.to_primitive(self, None);
primitive.equals(other, interpreter)
}
(_, ValueData::Object(_)) => {
let mut primitive = interpreter.to_primitive(other, None);
primitive.equals(self, interpreter)
}
_ => false,
}
}
}

impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
HalidOdat marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -139,3 +207,50 @@ pub fn same_value_non_number(x: &Value, y: &Value) -> bool {
_ => false,
}
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
#[test]
fn abstract_equality_comparison() {
use crate::{forward, Executor, Realm};
let realm = Realm::create();
let mut engine = Executor::new(realm);

assert_eq!(forward(&mut engine, "undefined == undefined"), "true");
assert_eq!(forward(&mut engine, "null == null"), "true");
assert_eq!(forward(&mut engine, "true == true"), "true");
assert_eq!(forward(&mut engine, "false == false"), "true");
assert_eq!(forward(&mut engine, "'foo' == 'foo'"), "true");
assert_eq!(forward(&mut engine, "0 == 0"), "true");
assert_eq!(forward(&mut engine, "+0 == -0"), "true");
assert_eq!(forward(&mut engine, "+0 == 0"), "true");
assert_eq!(forward(&mut engine, "-0 == 0"), "true");
assert_eq!(forward(&mut engine, "0 == false"), "true");
assert_eq!(forward(&mut engine, "'' == false"), "true");
assert_eq!(forward(&mut engine, "'' == 0"), "true");
assert_eq!(forward(&mut engine, "'17' == 17"), "true");
assert_eq!(forward(&mut engine, "[1,2] == '1,2'"), "true");
assert_eq!(forward(&mut engine, "new String('foo') == 'foo'"), "true");
assert_eq!(forward(&mut engine, "null == undefined"), "true");
assert_eq!(forward(&mut engine, "undefined == null"), "true");
assert_eq!(forward(&mut engine, "null == false"), "false");
Razican marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(
forward(&mut engine, "a = { foo: 'bar' }; b = { foo: 'bar'}; a == b"),
"false"
);
assert_eq!(
forward(&mut engine, "new String('foo') == new String('foo')"),
"false"
);
assert_eq!(forward(&mut engine, "0 == null"), "false");

// https://github.com/jasonwilliams/boa/issues/357
assert_eq!(forward(&mut engine, "0 == '-0'"), "true");
assert_eq!(forward(&mut engine, "0 == '+0'"), "true");
assert_eq!(forward(&mut engine, "'+0' == 0"), "true");
assert_eq!(forward(&mut engine, "'-0' == 0"), "true");

// https://github.com/jasonwilliams/boa/issues/393
// assert_eq!(forward(&mut engine, "0 == NaN"), "false");
// assert_eq!(forward(&mut engine, "'foo' == NaN"), "false");
// assert_eq!(forward(&mut engine, "NaN == NaN"), "false");
}
hello2dj marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 4 additions & 8 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,14 +446,10 @@ impl Executor for Interpreter {
let mut v_a = v_r_a.borrow_mut();
let mut v_b = v_r_b.borrow_mut();
Ok(Value::from(match *op {
CompOp::Equal if v_a.is_object() => v_r_a == v_r_b,
CompOp::Equal => v_a == v_b,
CompOp::NotEqual if v_a.is_object() => v_r_a != v_r_b,
CompOp::NotEqual => v_a != v_b,
CompOp::StrictEqual if v_a.is_object() => v_r_a == v_r_b,
CompOp::StrictEqual => v_a == v_b,
CompOp::StrictNotEqual if v_a.is_object() => v_r_a != v_r_b,
CompOp::StrictNotEqual => v_a != v_b,
CompOp::Equal => v_r_a.equals(v_b, self),
CompOp::NotEqual => !v_r_a.equals(v_b, self),
CompOp::StrictEqual => v_r_a.strict_equals(v_b),
CompOp::StrictNotEqual => !v_r_a.strict_equals(v_b),
CompOp::GreaterThan => v_a.to_number() > v_b.to_number(),
CompOp::GreaterThanOrEqual => v_a.to_number() >= v_b.to_number(),
CompOp::LessThan => v_a.to_number() < v_b.to_number(),
Expand Down