Browse Source

Fix abstract relational comparison (`<`, `>`, `<=` and `=>`) (#617)

pull/638/head
HalidOdat 4 years ago committed by GitHub
parent
commit
607a534cc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      boa/src/builtins/number/mod.rs
  2. 4
      boa/src/builtins/property/mod.rs
  3. 22
      boa/src/builtins/value/mod.rs
  4. 209
      boa/src/builtins/value/operations.rs
  5. 664
      boa/src/builtins/value/tests.rs
  6. 17
      boa/src/builtins/value/type.rs
  7. 28
      boa/src/exec/operator/mod.rs

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

@ -16,6 +16,7 @@
use super::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
value::AbstractRelation,
};
use crate::{
builtins::value::{ResultValue, Value},
@ -830,6 +831,7 @@ impl Number {
/// x (a Number) and y (a Number). It performs the following steps when called:
///
/// https://tc39.es/ecma262/#sec-numeric-types-number-equal
#[inline]
#[allow(clippy::float_cmp)]
pub(crate) fn equal(x: f64, y: f64) -> bool {
x == y
@ -861,6 +863,7 @@ impl Number {
/// x (a Number) and y (a Number). It performs the following steps when called:
///
/// https://tc39.es/ecma262/#sec-numeric-types-number-sameValueZero
#[inline]
#[allow(clippy::float_cmp)]
pub(crate) fn same_value_zero(x: f64, y: f64) -> bool {
if x.is_nan() && y.is_nan() {
@ -869,4 +872,28 @@ impl Number {
x == y
}
#[inline]
#[allow(clippy::float_cmp)]
pub(crate) fn less_than(x: f64, y: f64) -> AbstractRelation {
if x.is_nan() || y.is_nan() {
return AbstractRelation::Undefined;
}
if x == y || x == 0.0 && y == -0.0 || x == -0.0 && y == 0.0 {
return AbstractRelation::False;
}
if x.is_infinite() && x.is_sign_positive() {
return AbstractRelation::False;
}
if y.is_infinite() && y.is_sign_positive() {
return AbstractRelation::True;
}
if x.is_infinite() && x.is_sign_negative() {
return AbstractRelation::False;
}
if y.is_infinite() && y.is_sign_negative() {
return AbstractRelation::True;
}
(x < y).into()
}
}

4
boa/src/builtins/property/mod.rs

@ -14,8 +14,8 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes
use crate::builtins::value::rcstring::RcString;
use crate::builtins::value::rcsymbol::RcSymbol;
use crate::builtins::value::RcString;
use crate::builtins::value::RcSymbol;
use crate::builtins::Value;
use gc::{Finalize, Trace};
use std::fmt;

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

@ -5,10 +5,6 @@
#[cfg(test)]
mod tests;
pub mod val_type;
pub use crate::builtins::value::val_type::Type;
use crate::builtins::{
function::Function,
object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE},
@ -28,20 +24,22 @@ use std::{
str::FromStr,
};
pub mod conversions;
pub mod display;
pub mod equality;
pub mod hash;
pub mod operations;
pub mod rcbigint;
pub mod rcstring;
pub mod rcsymbol;
mod conversions;
mod display;
mod equality;
mod hash;
mod operations;
mod rcbigint;
mod rcstring;
mod rcsymbol;
mod r#type;
pub use conversions::*;
pub(crate) use display::display_obj;
pub use equality::*;
pub use hash::*;
pub use operations::*;
pub use r#type::Type;
pub use rcbigint::RcBigInt;
pub use rcstring::RcString;
pub use rcsymbol::RcSymbol;

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

@ -1,5 +1,5 @@
use super::*;
use crate::builtins::number::{f64_to_int32, f64_to_uint32};
use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number};
use crate::exec::PreferredType;
impl Value {
@ -405,4 +405,211 @@ impl Value {
pub fn not(&self, _: &mut Interpreter) -> ResultValue {
Ok(Self::boolean(!self.to_boolean()))
}
/// Abstract relational comparison
///
/// The comparison `x < y`, where `x` and `y` are values, produces `true`, `false`,
/// or `undefined` (which indicates that at least one operand is `NaN`).
///
/// In addition to `x` and `y` the algorithm takes a Boolean flag named `LeftFirst` as a parameter.
/// The flag is used to control the order in which operations with potentially visible side-effects
/// are performed upon `x` and `y`. It is necessary because ECMAScript specifies left to right evaluation
/// of expressions. The default value of LeftFirst is `true` and indicates that the `x` parameter
/// corresponds to an expression that occurs to the left of the `y` parameter's corresponding expression.
///
/// If `LeftFirst` is `false`, the reverse is the case and operations must be performed upon `y` before `x`.
///
/// More Information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-abstract-relational-comparison
pub fn abstract_relation(
&self,
other: &Self,
left_first: bool,
ctx: &mut Interpreter,
) -> Result<AbstractRelation, Value> {
Ok(match (self, other) {
// Fast path (for some common operations):
(Value::Integer(x), Value::Integer(y)) => (x < y).into(),
(Value::Integer(x), Value::Rational(y)) => Number::less_than(f64::from(*x), *y),
(Value::Rational(x), Value::Integer(y)) => Number::less_than(*x, f64::from(*y)),
(Value::Rational(x), Value::Rational(y)) => Number::less_than(*x, *y),
(Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(),
// Slow path:
(_, _) => {
let (px, py) = if left_first {
let px = ctx.to_primitive(self, PreferredType::Number)?;
let py = ctx.to_primitive(other, PreferredType::Number)?;
(px, py)
} else {
// NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
let py = ctx.to_primitive(other, PreferredType::Number)?;
let px = ctx.to_primitive(self, PreferredType::Number)?;
(px, py)
};
match (px, py) {
(Value::String(ref x), Value::String(ref y)) => {
if x.starts_with(y.as_str()) {
return Ok(AbstractRelation::False);
}
if y.starts_with(x.as_str()) {
return Ok(AbstractRelation::True);
}
for (x, y) in x.chars().zip(y.chars()) {
if x != y {
return Ok((x < y).into());
}
}
unreachable!()
}
(Value::BigInt(ref x), Value::String(ref y)) => {
if let Some(y) = string_to_bigint(&y) {
(*x.as_inner() < y).into()
} else {
AbstractRelation::Undefined
}
}
(Value::String(ref x), Value::BigInt(ref y)) => {
if let Some(x) = string_to_bigint(&x) {
(x < *y.as_inner()).into()
} else {
AbstractRelation::Undefined
}
}
(px, py) => match (ctx.to_numeric(&px)?, ctx.to_numeric(&py)?) {
(Value::Rational(x), Value::Rational(y)) => Number::less_than(x, y),
(Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(),
(Value::BigInt(ref x), Value::Rational(y)) => {
if y.is_nan() {
return Ok(AbstractRelation::Undefined);
}
if y.is_infinite() {
return Ok(y.is_sign_positive().into());
}
let n = if y.is_sign_negative() {
y.floor()
} else {
y.ceil()
};
(*x.as_inner() < BigInt::try_from(n).unwrap()).into()
}
(Value::Rational(x), Value::BigInt(ref y)) => {
if x.is_nan() {
return Ok(AbstractRelation::Undefined);
}
if x.is_infinite() {
return Ok(x.is_sign_negative().into());
}
let n = if x.is_sign_negative() {
x.floor()
} else {
x.ceil()
};
(BigInt::try_from(n).unwrap() < *y.as_inner()).into()
}
(_, _) => unreachable!("to_numeric should only retrun number and bigint"),
},
}
}
})
}
/// The less than operator (`<`) returns `true` if the left operand is less than the right operand,
/// and `false` otherwise.
///
/// More Information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn lt(&self, other: &Self, ctx: &mut Interpreter) -> Result<bool, Value> {
match self.abstract_relation(other, true, ctx)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
}
}
/// The less than or equal operator (`<=`) returns `true` if the left operand is less than
/// or equal to the right operand, and `false` otherwise.
///
/// More Information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than_or_equal
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn le(&self, other: &Self, ctx: &mut Interpreter) -> Result<bool, Value> {
match other.abstract_relation(self, false, ctx)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
}
}
/// The greater than operator (`>`) returns `true` if the left operand is greater than
/// the right operand, and `false` otherwise.
///
/// More Information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn gt(&self, other: &Self, ctx: &mut Interpreter) -> Result<bool, Value> {
match other.abstract_relation(self, false, ctx)? {
AbstractRelation::True => Ok(true),
AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
}
}
/// The greater than or equal operator (`>=`) returns `true` if the left operand is greater than
/// or equal to the right operand, and `false` otherwise.
///
/// More Information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than_or_equal
/// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
#[inline]
pub fn ge(&self, other: &Self, ctx: &mut Interpreter) -> Result<bool, Value> {
match self.abstract_relation(other, true, ctx)? {
AbstractRelation::False => Ok(true),
AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
}
}
}
/// The result of the [Abstract Relational Comparison][arc].
///
/// Comparison `x < y`, where `x` and `y` are values.
/// It produces `true`, `false`, or `undefined`
/// (which indicates that at least one operand is `NaN`).
///
/// [arc]: https://tc39.es/ecma262/#sec-abstract-relational-comparison
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AbstractRelation {
/// `x` is less than `y`
True,
/// `x` is **not** less than `y`
False,
/// Indicates that at least one operand is `NaN`
Undefined,
}
impl From<bool> for AbstractRelation {
#[inline]
fn from(value: bool) -> Self {
if value {
AbstractRelation::True
} else {
AbstractRelation::False
}
}
}

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

@ -502,3 +502,667 @@ toString: {
}"#
);
}
mod abstract_relational_comparison {
use super::*;
macro_rules! check_comparison {
($engine:ident, $string:expr => $expect:expr) => {
assert_eq!(
forward_val(&mut $engine, $string).unwrap().to_boolean(),
$expect
);
};
}
#[test]
fn number_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 < 2" => true);
check_comparison!(engine, "2 < 2" => false);
check_comparison!(engine, "3 < 2" => false);
check_comparison!(engine, "2 < 2.5" => true);
check_comparison!(engine, "2.5 < 2" => false);
}
#[test]
fn string_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1' < 2" => true);
check_comparison!(engine, "'2' < 2" => false);
check_comparison!(engine, "'3' < 2" => false);
check_comparison!(engine, "'2' < 2.5" => true);
check_comparison!(engine, "'2.5' < 2" => false);
}
#[test]
fn number_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 < '2'" => true);
check_comparison!(engine, "2 < '2'" => false);
check_comparison!(engine, "3 < '2'" => false);
check_comparison!(engine, "2 < '2.5'" => true);
check_comparison!(engine, "2.5 < '2'" => false);
}
#[test]
fn number_object_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) < '2'" => true);
check_comparison!(engine, "new Number(2) < '2'" => false);
check_comparison!(engine, "new Number(3) < '2'" => false);
check_comparison!(engine, "new Number(2) < '2.5'" => true);
check_comparison!(engine, "new Number(2.5) < '2'" => false);
}
#[test]
fn number_object_less_than_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) < new Number(2)" => true);
check_comparison!(engine, "new Number(2) < new Number(2)" => false);
check_comparison!(engine, "new Number(3) < new Number(2)" => false);
check_comparison!(engine, "new Number(2) < new Number(2.5)" => true);
check_comparison!(engine, "new Number(2.5) < new Number(2)" => false);
}
#[test]
fn string_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'hello' < 'hello'" => false);
check_comparison!(engine, "'hell' < 'hello'" => true);
check_comparison!(engine, "'hello, world' < 'world'" => true);
check_comparison!(engine, "'aa' < 'ab'" => true);
}
#[test]
fn string_object_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') < 'hello'" => false);
check_comparison!(engine, "new String('hell') < 'hello'" => true);
check_comparison!(engine, "new String('hello, world') < 'world'" => true);
check_comparison!(engine, "new String('aa') < 'ab'" => true);
}
#[test]
fn string_object_less_than_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') < new String('hello')" => false);
check_comparison!(engine, "new String('hell') < new String('hello')" => true);
check_comparison!(engine, "new String('hello, world') < new String('world')" => true);
check_comparison!(engine, "new String('aa') < new String('ab')" => true);
}
#[test]
fn bigint_less_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1n < 10" => true);
check_comparison!(engine, "10n < 10" => false);
check_comparison!(engine, "100n < 10" => false);
check_comparison!(engine, "10n < 10.9" => true);
}
#[test]
fn number_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "10 < 1n" => false);
check_comparison!(engine, "1 < 1n" => false);
check_comparison!(engine, "-1 < -1n" => false);
check_comparison!(engine, "-1.9 < -1n" => true);
}
#[test]
fn negative_infnity_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "-Infinity < -10000000000n" => true);
check_comparison!(engine, "-Infinity < (-1n << 100n)" => true);
}
#[test]
fn bigint_less_than_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n < NaN" => false);
check_comparison!(engine, "(1n << 100n) < NaN" => false);
}
#[test]
fn nan_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "NaN < -10000000000n" => false);
check_comparison!(engine, "NaN < (-1n << 100n)" => false);
}
#[test]
fn bigint_less_than_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n < Infinity" => true);
check_comparison!(engine, "(1n << 100n) < Infinity" => true);
}
#[test]
fn bigint_less_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n < '1000'" => false);
check_comparison!(engine, "1000n < '2000'" => true);
check_comparison!(engine, "1n < '-1'" => false);
check_comparison!(engine, "2n < '-1'" => false);
check_comparison!(engine, "-100n < 'InvalidBigInt'" => false);
}
#[test]
fn string_less_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1000' < 1000n" => false);
check_comparison!(engine, "'2000' < 1000n" => false);
check_comparison!(engine, "'500' < 1000n" => true);
check_comparison!(engine, "'-1' < 1n" => true);
check_comparison!(engine, "'-1' < 2n" => true);
check_comparison!(engine, "'InvalidBigInt' < -100n" => false);
}
// -------------------------------------------
#[test]
fn number_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 <= 2" => true);
check_comparison!(engine, "2 <= 2" => true);
check_comparison!(engine, "3 <= 2" => false);
check_comparison!(engine, "2 <= 2.5" => true);
check_comparison!(engine, "2.5 <= 2" => false);
}
#[test]
fn string_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1' <= 2" => true);
check_comparison!(engine, "'2' <= 2" => true);
check_comparison!(engine, "'3' <= 2" => false);
check_comparison!(engine, "'2' <= 2.5" => true);
check_comparison!(engine, "'2.5' < 2" => false);
}
#[test]
fn number_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 <= '2'" => true);
check_comparison!(engine, "2 <= '2'" => true);
check_comparison!(engine, "3 <= '2'" => false);
check_comparison!(engine, "2 <= '2.5'" => true);
check_comparison!(engine, "2.5 <= '2'" => false);
}
#[test]
fn number_object_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) <= '2'" => true);
check_comparison!(engine, "new Number(2) <= '2'" => true);
check_comparison!(engine, "new Number(3) <= '2'" => false);
check_comparison!(engine, "new Number(2) <= '2.5'" => true);
check_comparison!(engine, "new Number(2.5) <= '2'" => false);
}
#[test]
fn number_object_less_than_number_or_equal_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) <= new Number(2)" => true);
check_comparison!(engine, "new Number(2) <= new Number(2)" => true);
check_comparison!(engine, "new Number(3) <= new Number(2)" => false);
check_comparison!(engine, "new Number(2) <= new Number(2.5)" => true);
check_comparison!(engine, "new Number(2.5) <= new Number(2)" => false);
}
#[test]
fn string_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'hello' <= 'hello'" => true);
check_comparison!(engine, "'hell' <= 'hello'" => true);
check_comparison!(engine, "'hello, world' <= 'world'" => true);
check_comparison!(engine, "'aa' <= 'ab'" => true);
}
#[test]
fn string_object_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') <= 'hello'" => true);
check_comparison!(engine, "new String('hell') <= 'hello'" => true);
check_comparison!(engine, "new String('hello, world') <= 'world'" => true);
check_comparison!(engine, "new String('aa') <= 'ab'" => true);
}
#[test]
fn string_object_less_than_string_or_equal_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') <= new String('hello')" => true);
check_comparison!(engine, "new String('hell') <= new String('hello')" => true);
check_comparison!(engine, "new String('hello, world') <= new String('world')" => true);
check_comparison!(engine, "new String('aa') <= new String('ab')" => true);
}
#[test]
fn bigint_less_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1n <= 10" => true);
check_comparison!(engine, "10n <= 10" => true);
check_comparison!(engine, "100n <= 10" => false);
check_comparison!(engine, "10n <= 10.9" => true);
}
#[test]
fn number_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "10 <= 1n" => false);
check_comparison!(engine, "1 <= 1n" => true);
check_comparison!(engine, "-1 <= -1n" => true);
check_comparison!(engine, "-1.9 <= -1n" => true);
}
#[test]
fn negative_infnity_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "-Infinity <= -10000000000n" => true);
check_comparison!(engine, "-Infinity <= (-1n << 100n)" => true);
}
#[test]
fn bigint_less_than_or_equal_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n <= NaN" => false);
check_comparison!(engine, "(1n << 100n) <= NaN" => false);
}
#[test]
fn nan_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "NaN <= -10000000000n" => false);
check_comparison!(engine, "NaN <= (-1n << 100n)" => false);
}
#[test]
fn bigint_less_than_or_equal_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n <= Infinity" => true);
check_comparison!(engine, "(1n << 100n) <= Infinity" => true);
}
#[test]
fn bigint_less_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n <= '1000'" => true);
check_comparison!(engine, "1000n <= '2000'" => true);
check_comparison!(engine, "1n <= '-1'" => false);
check_comparison!(engine, "2n <= '-1'" => false);
check_comparison!(engine, "-100n <= 'InvalidBigInt'" => false);
}
#[test]
fn string_less_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1000' <= 1000n" => true);
check_comparison!(engine, "'2000' <= 1000n" => false);
check_comparison!(engine, "'500' <= 1000n" => true);
check_comparison!(engine, "'-1' <= 1n" => true);
check_comparison!(engine, "'-1' <= 2n" => true);
check_comparison!(engine, "'InvalidBigInt' <= -100n" => false);
}
// -------------------------------------------
#[test]
fn number_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 > 2" => false);
check_comparison!(engine, "2 > 2" => false);
check_comparison!(engine, "3 > 2" => true);
check_comparison!(engine, "2 > 2.5" => false);
check_comparison!(engine, "2.5 > 2" => true);
}
#[test]
fn string_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1' > 2" => false);
check_comparison!(engine, "'2' > 2" => false);
check_comparison!(engine, "'3' > 2" => true);
check_comparison!(engine, "'2' > 2.5" => false);
check_comparison!(engine, "'2.5' > 2" => true);
}
#[test]
fn number_less_greater_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 > '2'" => false);
check_comparison!(engine, "2 > '2'" => false);
check_comparison!(engine, "3 > '2'" => true);
check_comparison!(engine, "2 > '2.5'" => false);
check_comparison!(engine, "2.5 > '2'" => true);
}
#[test]
fn number_object_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) > '2'" => false);
check_comparison!(engine, "new Number(2) > '2'" => false);
check_comparison!(engine, "new Number(3) > '2'" => true);
check_comparison!(engine, "new Number(2) > '2.5'" => false);
check_comparison!(engine, "new Number(2.5) > '2'" => true);
}
#[test]
fn number_object_greater_than_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) > new Number(2)" => false);
check_comparison!(engine, "new Number(2) > new Number(2)" => false);
check_comparison!(engine, "new Number(3) > new Number(2)" => true);
check_comparison!(engine, "new Number(2) > new Number(2.5)" => false);
check_comparison!(engine, "new Number(2.5) > new Number(2)" => true);
}
#[test]
fn string_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'hello' > 'hello'" => false);
check_comparison!(engine, "'hell' > 'hello'" => false);
check_comparison!(engine, "'hello, world' > 'world'" => false);
check_comparison!(engine, "'aa' > 'ab'" => false);
check_comparison!(engine, "'ab' > 'aa'" => true);
}
#[test]
fn string_object_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') > 'hello'" => false);
check_comparison!(engine, "new String('hell') > 'hello'" => false);
check_comparison!(engine, "new String('hello, world') > 'world'" => false);
check_comparison!(engine, "new String('aa') > 'ab'" => false);
check_comparison!(engine, "new String('ab') > 'aa'" => true);
}
#[test]
fn string_object_greater_than_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') > new String('hello')" => false);
check_comparison!(engine, "new String('hell') > new String('hello')" => false);
check_comparison!(engine, "new String('hello, world') > new String('world')" => false);
check_comparison!(engine, "new String('aa') > new String('ab')" => false);
check_comparison!(engine, "new String('ab') > new String('aa')" => true);
}
#[test]
fn bigint_greater_than_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1n > 10" => false);
check_comparison!(engine, "10n > 10" => false);
check_comparison!(engine, "100n > 10" => true);
check_comparison!(engine, "10n > 10.9" => false);
}
#[test]
fn number_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "10 > 1n" => true);
check_comparison!(engine, "1 > 1n" => false);
check_comparison!(engine, "-1 > -1n" => false);
check_comparison!(engine, "-1.9 > -1n" => false);
}
#[test]
fn negative_infnity_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "-Infinity > -10000000000n" => false);
check_comparison!(engine, "-Infinity > (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n > NaN" => false);
check_comparison!(engine, "(1n << 100n) > NaN" => false);
}
#[test]
fn nan_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "NaN > -10000000000n" => false);
check_comparison!(engine, "NaN > (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n > Infinity" => false);
check_comparison!(engine, "(1n << 100n) > Infinity" => false);
}
#[test]
fn bigint_greater_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n > '1000'" => false);
check_comparison!(engine, "1000n > '2000'" => false);
check_comparison!(engine, "1n > '-1'" => true);
check_comparison!(engine, "2n > '-1'" => true);
check_comparison!(engine, "-100n > 'InvalidBigInt'" => false);
}
#[test]
fn string_greater_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1000' > 1000n" => false);
check_comparison!(engine, "'2000' > 1000n" => true);
check_comparison!(engine, "'500' > 1000n" => false);
check_comparison!(engine, "'-1' > 1n" => false);
check_comparison!(engine, "'-1' > 2n" => false);
check_comparison!(engine, "'InvalidBigInt' > -100n" => false);
}
// ----------------------------------------------
#[test]
fn number_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 >= 2" => false);
check_comparison!(engine, "2 >= 2" => true);
check_comparison!(engine, "3 >= 2" => true);
check_comparison!(engine, "2 >= 2.5" => false);
check_comparison!(engine, "2.5 >= 2" => true);
}
#[test]
fn string_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1' >= 2" => false);
check_comparison!(engine, "'2' >= 2" => true);
check_comparison!(engine, "'3' >= 2" => true);
check_comparison!(engine, "'2' >= 2.5" => false);
check_comparison!(engine, "'2.5' >= 2" => true);
}
#[test]
fn number_less_greater_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1 >= '2'" => false);
check_comparison!(engine, "2 >= '2'" => true);
check_comparison!(engine, "3 >= '2'" => true);
check_comparison!(engine, "2 >= '2.5'" => false);
check_comparison!(engine, "2.5 >= '2'" => true);
}
#[test]
fn number_object_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) >= '2'" => false);
check_comparison!(engine, "new Number(2) >= '2'" => true);
check_comparison!(engine, "new Number(3) >= '2'" => true);
check_comparison!(engine, "new Number(2) >= '2.5'" => false);
check_comparison!(engine, "new Number(2.5) >= '2'" => true);
}
#[test]
fn number_object_greater_than_or_equal_number_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new Number(1) >= new Number(2)" => false);
check_comparison!(engine, "new Number(2) >= new Number(2)" => true);
check_comparison!(engine, "new Number(3) >= new Number(2)" => true);
check_comparison!(engine, "new Number(2) >= new Number(2.5)" => false);
check_comparison!(engine, "new Number(2.5) >= new Number(2)" => true);
}
#[test]
fn string_greater_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'hello' >= 'hello'" => true);
check_comparison!(engine, "'hell' >= 'hello'" => false);
check_comparison!(engine, "'hello, world' >= 'world'" => false);
check_comparison!(engine, "'aa' >= 'ab'" => false);
check_comparison!(engine, "'ab' >= 'aa'" => true);
}
#[test]
fn string_object_greater_or_equal_than_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') >= 'hello'" => true);
check_comparison!(engine, "new String('hell') >= 'hello'" => false);
check_comparison!(engine, "new String('hello, world') >= 'world'" => false);
check_comparison!(engine, "new String('aa') >= 'ab'" => false);
check_comparison!(engine, "new String('ab') >= 'aa'" => true);
}
#[test]
fn string_object_greater_than_or_equal_string_object() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "new String('hello') >= new String('hello')" => true);
check_comparison!(engine, "new String('hell') >= new String('hello')" => false);
check_comparison!(engine, "new String('hello, world') >= new String('world')" => false);
check_comparison!(engine, "new String('aa') >= new String('ab')" => false);
check_comparison!(engine, "new String('ab') >= new String('aa')" => true);
}
#[test]
fn bigint_greater_than_or_equal_number() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1n >= 10" => false);
check_comparison!(engine, "10n >= 10" => true);
check_comparison!(engine, "100n >= 10" => true);
check_comparison!(engine, "10n >= 10.9" => false);
}
#[test]
fn number_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "10 >= 1n" => true);
check_comparison!(engine, "1 >= 1n" => true);
check_comparison!(engine, "-1 >= -1n" => true);
check_comparison!(engine, "-1.9 >= -1n" => false);
}
#[test]
fn negative_infnity_greater_or_equal_than_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "-Infinity >= -10000000000n" => false);
check_comparison!(engine, "-Infinity >= (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_or_equal_infinity() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n >= NaN" => false);
check_comparison!(engine, "(1n << 100n) >= NaN" => false);
}
#[test]
fn nan_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "NaN >= -10000000000n" => false);
check_comparison!(engine, "NaN >= (-1n << 100n)" => false);
}
#[test]
fn bigint_greater_than_or_equal_nan() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n >= Infinity" => false);
check_comparison!(engine, "(1n << 100n) >= Infinity" => false);
}
#[test]
fn bigint_greater_than_or_equal_string() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "1000n >= '1000'" => true);
check_comparison!(engine, "1000n >= '2000'" => false);
check_comparison!(engine, "1n >= '-1'" => true);
check_comparison!(engine, "2n >= '-1'" => true);
check_comparison!(engine, "-100n >= 'InvalidBigInt'" => false);
}
#[test]
fn string_greater_than_or_equal_bigint() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
check_comparison!(engine, "'1000' >= 1000n" => true);
check_comparison!(engine, "'2000' >= 1000n" => true);
check_comparison!(engine, "'500' >= 1000n" => false);
check_comparison!(engine, "'-1' >= 1n" => false);
check_comparison!(engine, "'-1' >= 2n" => false);
check_comparison!(engine, "'InvalidBigInt' >= -100n" => false);
}
}

17
boa/src/builtins/value/val_type.rs → boa/src/builtins/value/type.rs

@ -1,9 +1,8 @@
use crate::builtins::value::Value;
use std::ops::Deref;
use super::Value;
/// Possible types of val as defined at https://tc39.es/ecma262/#sec-typeof-operator.
/// Possible types of values as defined at https://tc39.es/ecma262/#sec-typeof-operator.
/// Note that an object which implements call is referred to here as 'Function'.
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Type {
Undefined,
Null,
@ -14,11 +13,10 @@ pub enum Type {
BigInt,
Object,
Function,
Date,
}
impl Type {
pub fn as_str(&self) -> &str {
pub fn as_str(self) -> &'static str {
match self {
Self::Number => "number",
Self::String => "string",
@ -29,7 +27,6 @@ impl Type {
Self::Function => "function",
Self::Object => "object",
Self::BigInt => "bigint",
Self::Date => "date",
}
}
}
@ -48,14 +45,14 @@ impl Value {
Self::Symbol(_) => Type::Symbol,
Self::Null => Type::Null,
Self::Undefined => Type::Undefined,
Self::Object(ref o) => {
if o.deref().borrow().is_callable() {
Self::BigInt(_) => Type::BigInt,
Self::Object(ref object) => {
if object.borrow().is_function() {
Type::Function
} else {
Type::Object
}
}
Self::BigInt(_) => Type::BigInt,
}
}
}

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

@ -77,26 +77,26 @@ impl Executable for BinOp {
}
}
op::BinOp::Comp(op) => {
let v_a = self.lhs().run(interpreter)?;
let v_b = self.rhs().run(interpreter)?;
let x = self.lhs().run(interpreter)?;
let y = self.rhs().run(interpreter)?;
Ok(Value::from(match op {
CompOp::Equal => v_a.equals(&v_b, interpreter)?,
CompOp::NotEqual => !v_a.equals(&v_b, interpreter)?,
CompOp::StrictEqual => v_a.strict_equals(&v_b),
CompOp::StrictNotEqual => !v_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(),
CompOp::LessThanOrEqual => v_a.to_number() <= v_b.to_number(),
CompOp::Equal => x.equals(&y, interpreter)?,
CompOp::NotEqual => !x.equals(&y, interpreter)?,
CompOp::StrictEqual => x.strict_equals(&y),
CompOp::StrictNotEqual => !x.strict_equals(&y),
CompOp::GreaterThan => x.gt(&y, interpreter)?,
CompOp::GreaterThanOrEqual => x.ge(&y, interpreter)?,
CompOp::LessThan => x.lt(&y, interpreter)?,
CompOp::LessThanOrEqual => x.le(&y, interpreter)?,
CompOp::In => {
if !v_b.is_object() {
if !y.is_object() {
return interpreter.throw_type_error(format!(
"right-hand side of 'in' should be an object, got {}",
v_b.get_type().as_str()
y.get_type().as_str()
));
}
let key = interpreter.to_property_key(&v_a)?;
interpreter.has_property(&v_b, &key)
let key = interpreter.to_property_key(&x)?;
interpreter.has_property(&y, &key)
}
}))
}

Loading…
Cancel
Save