Skip to content

Commit

Permalink
Merge 88da795 into 36ac8dc
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Aug 9, 2020
2 parents 36ac8dc + 88da795 commit f0a218b
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 15 deletions.
27 changes: 27 additions & 0 deletions boa/src/builtins/number/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use super::{
function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
value::TriState,
};
use crate::{
builtins::value::{ResultValue, Value},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand All @@ -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()
}
}
130 changes: 129 additions & 1 deletion boa/src/builtins/value/operations.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<TriState, Value> {
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<bool, Value> {
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<bool, Value> {
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<bool, Value> {
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<bool, Value> {
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<bool> for TriState {
fn from(value: bool) -> Self {
if value {
TriState::True
} else {
TriState::False
}
}
}
28 changes: 14 additions & 14 deletions boa/src/exec/operator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}))
}
Expand Down

0 comments on commit f0a218b

Please sign in to comment.