From 88da795887481f91bab2f6468303e1db847174e9 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 9 Aug 2020 15:14:22 +0200 Subject: [PATCH 1/8] Fix abstract relational comparison --- boa/src/builtins/number/mod.rs | 27 ++++++ boa/src/builtins/value/operations.rs | 130 ++++++++++++++++++++++++++- boa/src/exec/operator/mod.rs | 28 +++--- 3 files changed, 170 insertions(+), 15 deletions(-) diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index d34b6065913..82d0d50d2ff 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -16,6 +16,7 @@ use super::{ function::{make_builtin_fn, make_constructor_fn}, object::ObjectData, + value::TriState, }; 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) -> TriState { + if x.is_nan() || y.is_nan() { + return TriState::Undefined; + } + if x == y || x == 0.0 && y == -0.0 || x == -0.0 && y == 0.0 { + return TriState::False; + } + if x.is_infinite() && x.is_sign_positive() { + return TriState::False; + } + if y.is_infinite() && y.is_sign_positive() { + return TriState::True; + } + if x.is_infinite() && x.is_sign_negative() { + return TriState::False; + } + if y.is_infinite() && y.is_sign_negative() { + return TriState::True; + } + (x < y).into() + } } diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index e4f562865e5..f1db4bcc89b 100644 --- a/boa/src/builtins/value/operations.rs +++ b/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,132 @@ impl Value { pub fn not(&self, _: &mut Interpreter) -> ResultValue { Ok(Self::boolean(!self.to_boolean())) } + + pub fn abstract_relation( + &self, + other: &Self, + left_first: bool, + ctx: &mut Interpreter, + ) -> Result { + 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(TriState::False); + } + if y.starts_with(x.as_str()) { + return Ok(TriState::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)) => { + Ok(if let Some(y) = string_to_bigint(&y) { + (*x.as_inner() < y).into() + } else { + TriState::Undefined + }) + } + (Value::String(ref x), Value::BigInt(ref y)) => { + Ok(if let Some(x) = string_to_bigint(&x) { + (x < *y.as_inner()).into() + } else { + TriState::Undefined + }) + } + (px, py) => { + let nx = ctx.to_numeric(&px)?; + let ny = ctx.to_numeric(&py)?; + Ok(match (nx, ny) { + (Value::Integer(x), Value::Integer(y)) => (x < y).into(), + (Value::Integer(x), Value::Rational(y)) => Number::less_than(x.into(), y), + (Value::Rational(x), Value::Integer(y)) => Number::less_than(x, y.into()), + (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(TriState::Undefined); + } + if y.is_infinite() { + return Ok(y.is_sign_positive().into()); + } + (*x.as_inner() < BigInt::try_from(y.trunc()).unwrap()).into() + } + (Value::Rational(x), Value::BigInt(ref y)) => { + if x.is_nan() { + return Ok(TriState::Undefined); + } + if x.is_infinite() { + return Ok(x.is_sign_positive().into()); + } + (BigInt::try_from(x.trunc()).unwrap() < *y.as_inner()).into() + } + (_, _) => unreachable!(), + }) + } + } + } + + #[inline] + pub fn lt(&self, other: &Self, ctx: &mut Interpreter) -> Result { + match self.abstract_relation(other, true, ctx)? { + TriState::True => Ok(true), + TriState::False | TriState::Undefined => Ok(false), + } + } + + #[inline] + pub fn le(&self, other: &Self, ctx: &mut Interpreter) -> Result { + match other.abstract_relation(self, false, ctx)? { + TriState::False => Ok(true), + TriState::True | TriState::Undefined => Ok(false), + } + } + + #[inline] + pub fn gt(&self, other: &Self, ctx: &mut Interpreter) -> Result { + match other.abstract_relation(self, false, ctx)? { + TriState::True => Ok(true), + TriState::False | TriState::Undefined => Ok(false), + } + } + + #[inline] + pub fn ge(&self, other: &Self, ctx: &mut Interpreter) -> Result { + match self.abstract_relation(other, true, ctx)? { + TriState::False => Ok(true), + TriState::True | TriState::Undefined => Ok(false), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum TriState { + False, + True, + Undefined, +} + +impl From for TriState { + fn from(value: bool) -> Self { + if value { + TriState::True + } else { + TriState::False + } + } } diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index cbbfd7617fe..0b5a8c9671b 100644 --- a/boa/src/exec/operator/mod.rs +++ b/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) } })) } From a1489be60fa351d9daa6f890434a85ae4a9e4918 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 9 Aug 2020 19:06:45 +0200 Subject: [PATCH 2/8] Added documentation --- boa/src/builtins/number/mod.rs | 16 +-- boa/src/builtins/property/mod.rs | 4 +- boa/src/builtins/value/mod.rs | 22 ++-- boa/src/builtins/value/operations.rs | 104 ++++++++++++++---- .../builtins/value/{val_type.rs => type.rs} | 2 +- 5 files changed, 105 insertions(+), 43 deletions(-) rename boa/src/builtins/value/{val_type.rs => type.rs} (98%) diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 82d0d50d2ff..14409c44cf2 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -16,7 +16,7 @@ use super::{ function::{make_builtin_fn, make_constructor_fn}, object::ObjectData, - value::TriState, + value::AbstractRelation, }; use crate::{ builtins::value::{ResultValue, Value}, @@ -875,24 +875,24 @@ impl Number { #[inline] #[allow(clippy::float_cmp)] - pub(crate) fn less_than(x: f64, y: f64) -> TriState { + pub(crate) fn less_than(x: f64, y: f64) -> AbstractRelation { if x.is_nan() || y.is_nan() { - return TriState::Undefined; + return AbstractRelation::Undefined; } if x == y || x == 0.0 && y == -0.0 || x == -0.0 && y == 0.0 { - return TriState::False; + return AbstractRelation::False; } if x.is_infinite() && x.is_sign_positive() { - return TriState::False; + return AbstractRelation::False; } if y.is_infinite() && y.is_sign_positive() { - return TriState::True; + return AbstractRelation::True; } if x.is_infinite() && x.is_sign_negative() { - return TriState::False; + return AbstractRelation::False; } if y.is_infinite() && y.is_sign_negative() { - return TriState::True; + return AbstractRelation::True; } (x < y).into() } diff --git a/boa/src/builtins/property/mod.rs b/boa/src/builtins/property/mod.rs index 0cfaa49b962..0cc272bcdaf 100644 --- a/boa/src/builtins/property/mod.rs +++ b/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; diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 498b019bbc3..b2016f31495 100644 --- a/boa/src/builtins/value/mod.rs +++ b/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; diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index f1db4bcc89b..5b632d79768 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -406,12 +406,29 @@ impl Value { 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 { + ) -> Result { let (px, py) = if left_first { let px = ctx.to_primitive(self, PreferredType::Number)?; let py = ctx.to_primitive(other, PreferredType::Number)?; @@ -426,10 +443,10 @@ impl Value { match (px, py) { (Value::String(ref x), Value::String(ref y)) => { if x.starts_with(y.as_str()) { - return Ok(TriState::False); + return Ok(AbstractRelation::False); } if y.starts_with(x.as_str()) { - return Ok(TriState::True); + return Ok(AbstractRelation::True); } for (x, y) in x.chars().zip(y.chars()) { if x != y { @@ -442,14 +459,14 @@ impl Value { Ok(if let Some(y) = string_to_bigint(&y) { (*x.as_inner() < y).into() } else { - TriState::Undefined + AbstractRelation::Undefined }) } (Value::String(ref x), Value::BigInt(ref y)) => { Ok(if let Some(x) = string_to_bigint(&x) { (x < *y.as_inner()).into() } else { - TriState::Undefined + AbstractRelation::Undefined }) } (px, py) => { @@ -463,7 +480,7 @@ impl Value { (Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(), (Value::BigInt(ref x), Value::Rational(y)) => { if y.is_nan() { - return Ok(TriState::Undefined); + return Ok(AbstractRelation::Undefined); } if y.is_infinite() { return Ok(y.is_sign_positive().into()); @@ -472,7 +489,7 @@ impl Value { } (Value::Rational(x), Value::BigInt(ref y)) => { if x.is_nan() { - return Ok(TriState::Undefined); + return Ok(AbstractRelation::Undefined); } if x.is_infinite() { return Ok(x.is_sign_positive().into()); @@ -485,52 +502,99 @@ impl Value { } } + /// 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 { match self.abstract_relation(other, true, ctx)? { - TriState::True => Ok(true), - TriState::False | TriState::Undefined => Ok(false), + 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 { match other.abstract_relation(self, false, ctx)? { - TriState::False => Ok(true), - TriState::True | TriState::Undefined => Ok(false), + 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 { match other.abstract_relation(self, false, ctx)? { - TriState::True => Ok(true), - TriState::False | TriState::Undefined => Ok(false), + 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 { match self.abstract_relation(other, true, ctx)? { - TriState::False => Ok(true), - TriState::True | TriState::Undefined => Ok(false), + 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 TriState { - False, +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 for TriState { +impl From for AbstractRelation { + #[inline] fn from(value: bool) -> Self { if value { - TriState::True + AbstractRelation::True } else { - TriState::False + AbstractRelation::False } } } diff --git a/boa/src/builtins/value/val_type.rs b/boa/src/builtins/value/type.rs similarity index 98% rename from boa/src/builtins/value/val_type.rs rename to boa/src/builtins/value/type.rs index 2861abd338e..776417abcb8 100644 --- a/boa/src/builtins/value/val_type.rs +++ b/boa/src/builtins/value/type.rs @@ -1,4 +1,4 @@ -use crate::builtins::value::Value; +use super::Value; use std::ops::Deref; /// Possible types of val as defined at https://tc39.es/ecma262/#sec-typeof-operator. From c12edffa0dfbe442113d37bf74083042622e7310 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 9 Aug 2020 19:18:20 +0200 Subject: [PATCH 3/8] Removed Type::Date in value --- boa/src/builtins/value/type.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/boa/src/builtins/value/type.rs b/boa/src/builtins/value/type.rs index 776417abcb8..6ebeac57520 100644 --- a/boa/src/builtins/value/type.rs +++ b/boa/src/builtins/value/type.rs @@ -1,9 +1,8 @@ use super::Value; -use std::ops::Deref; -/// 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, } } } From 4adebbeafb859e2fdc4f75770fd6ea811a8dd2a1 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 10 Aug 2020 14:50:41 +0200 Subject: [PATCH 4/8] Fixed bug and added less than operator tests --- boa/src/builtins/value/operations.rs | 2 +- boa/src/builtins/value/tests.rs | 172 +++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index 5b632d79768..a041acd417f 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -492,7 +492,7 @@ impl Value { return Ok(AbstractRelation::Undefined); } if x.is_infinite() { - return Ok(x.is_sign_positive().into()); + return Ok(x.is_sign_negative().into()); } (BigInt::try_from(x.trunc()).unwrap() < *y.as_inner()).into() } diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index dbc65962335..5c73d54047c 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -502,3 +502,175 @@ 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" => false); + } + + #[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" => false); + } + + #[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); + } +} From 329819e7f66d511cf475884e642877280ac19ec9 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 10 Aug 2020 15:01:44 +0200 Subject: [PATCH 5/8] Added test for less than or equal oerator --- boa/src/builtins/value/tests.rs | 162 ++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 5c73d54047c..928528a785f 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -673,4 +673,166 @@ mod abstract_relational_comparison { 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); + } } From d2e78a60f171f0482228ca6b4d94db54133abacc Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 10 Aug 2020 15:20:07 +0200 Subject: [PATCH 6/8] Added tests for greater than operator --- boa/src/builtins/value/tests.rs | 165 ++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 928528a785f..9b31e045036 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -835,4 +835,169 @@ mod abstract_relational_comparison { 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); + } } From ce26592df20d2a8ffa9f66d485decc41f7660dc3 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 10 Aug 2020 16:01:02 +0200 Subject: [PATCH 7/8] Added tests for greater than or equal operator --- boa/src/builtins/value/operations.rs | 14 ++- boa/src/builtins/value/tests.rs | 169 ++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 4 deletions(-) diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index a041acd417f..bcc0147827b 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -485,7 +485,12 @@ impl Value { if y.is_infinite() { return Ok(y.is_sign_positive().into()); } - (*x.as_inner() < BigInt::try_from(y.trunc()).unwrap()).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() { @@ -494,7 +499,12 @@ impl Value { if x.is_infinite() { return Ok(x.is_sign_negative().into()); } - (BigInt::try_from(x.trunc()).unwrap() < *y.as_inner()).into() + let n = if x.is_sign_negative() { + x.floor() + } else { + x.ceil() + }; + (BigInt::try_from(n).unwrap() < *y.as_inner()).into() } (_, _) => unreachable!(), }) diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 9b31e045036..6401b72cbef 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -606,7 +606,7 @@ mod abstract_relational_comparison { check_comparison!(engine, "1n < 10" => true); check_comparison!(engine, "10n < 10" => false); check_comparison!(engine, "100n < 10" => false); - check_comparison!(engine, "10n < 10.9" => false); + check_comparison!(engine, "10n < 10.9" => true); } #[test] @@ -616,7 +616,7 @@ mod abstract_relational_comparison { check_comparison!(engine, "10 < 1n" => false); check_comparison!(engine, "1 < 1n" => false); check_comparison!(engine, "-1 < -1n" => false); - check_comparison!(engine, "-1.9 < -1n" => false); + check_comparison!(engine, "-1.9 < -1n" => true); } #[test] @@ -1000,4 +1000,169 @@ mod abstract_relational_comparison { 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); + } } From 1089122233b8840043311644bea3389a044cd7df Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 10 Aug 2020 16:58:29 +0200 Subject: [PATCH 8/8] Added fast path for common operations --- boa/src/builtins/value/operations.rs | 151 ++++++++++++++------------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index bcc0147827b..c8ad4c39b53 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -429,87 +429,92 @@ impl Value { left_first: bool, ctx: &mut Interpreter, ) -> Result { - 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)) => { - Ok(if let Some(y) = string_to_bigint(&y) { - (*x.as_inner() < y).into() - } else { - AbstractRelation::Undefined - }) - } - (Value::String(ref x), Value::BigInt(ref y)) => { - Ok(if let Some(x) = string_to_bigint(&x) { - (x < *y.as_inner()).into() + 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 { - AbstractRelation::Undefined - }) - } - (px, py) => { - let nx = ctx.to_numeric(&px)?; - let ny = ctx.to_numeric(&py)?; - Ok(match (nx, ny) { - (Value::Integer(x), Value::Integer(y)) => (x < y).into(), - (Value::Integer(x), Value::Rational(y)) => Number::less_than(x.into(), y), - (Value::Rational(x), Value::Integer(y)) => Number::less_than(x, y.into()), - (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); + // 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.is_infinite() { - return Ok(y.is_sign_positive().into()); + if y.starts_with(x.as_str()) { + return Ok(AbstractRelation::True); } - 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); + for (x, y) in x.chars().zip(y.chars()) { + if x != y { + return Ok((x < y).into()); + } } - if x.is_infinite() { - return Ok(x.is_sign_negative().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 } - let n = if x.is_sign_negative() { - x.floor() + } + (Value::String(ref x), Value::BigInt(ref y)) => { + if let Some(x) = string_to_bigint(&x) { + (x < *y.as_inner()).into() } else { - x.ceil() - }; - (BigInt::try_from(n).unwrap() < *y.as_inner()).into() + AbstractRelation::Undefined + } } - (_, _) => unreachable!(), - }) + (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,