Skip to content

Commit

Permalink
Made to_primitive throw errors
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Jun 19, 2020
1 parent 437e25b commit c035f1a
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 48 deletions.
24 changes: 14 additions & 10 deletions boa/src/builtins/value/equality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ impl Value {
/// This method is executed when doing abstract equality comparisons with the `==` operator.
/// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
#[allow(clippy::float_cmp)]
pub fn equals(&self, other: &Self, interpreter: &mut Interpreter) -> bool {
pub fn equals(&self, other: &Self, interpreter: &mut Interpreter) -> Result<bool, Value> {
// 1. If Type(x) is the same as Type(y), then
// a. Return the result of performing Strict Equality Comparison x === y.
if self.get_type() == other.get_type() {
return self.strict_equals(other);
return Ok(self.strict_equals(other));
}

match (self, other) {
Ok(match (self, other) {
// 2. If x is null and y is undefined, return true.
// 3. If x is undefined and y is null, return true.
(Self::Null, Self::Undefined) | (Self::Undefined, Self::Null) => true,
Expand Down Expand Up @@ -82,23 +82,27 @@ impl Value {
},

// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
(Self::Boolean(_), _) => other.equals(&Value::from(self.to_integer()), interpreter),
(Self::Boolean(_), _) => {
return other.equals(&Value::from(self.to_integer()), interpreter)
}

// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
(_, Self::Boolean(_)) => self.equals(&Value::from(other.to_integer()), interpreter),
(_, Self::Boolean(_)) => {
return self.equals(&Value::from(other.to_integer()), interpreter)
}

// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result
// of the comparison x == ? ToPrimitive(y).
(Self::Object(_), _) => {
let primitive = interpreter.to_primitive(self, PreferredType::Default);
primitive.equals(other, interpreter)
let primitive = interpreter.to_primitive(self, PreferredType::Default)?;
return primitive.equals(other, interpreter);
}

// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result
// of the comparison ? ToPrimitive(x) == y.
(_, Self::Object(_)) => {
let primitive = interpreter.to_primitive(other, PreferredType::Default);
primitive.equals(self, interpreter)
let primitive = interpreter.to_primitive(other, PreferredType::Default)?;
return primitive.equals(self, interpreter);
}

// 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then
Expand All @@ -111,7 +115,7 @@ impl Value {

// 13. Return false.
_ => false,
}
})
}
}

Expand Down
90 changes: 54 additions & 36 deletions boa/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl Interpreter {
Value::Symbol(_) => Err(self.construct_type_error("can't convert symbol to string")),
Value::BigInt(ref bigint) => Ok(RcString::from(bigint.to_string())),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::String);
let primitive = self.to_primitive(value, PreferredType::String)?;
self.to_string(&primitive)
}
}
Expand Down Expand Up @@ -213,7 +213,7 @@ impl Interpreter {
}
Value::BigInt(b) => Ok(b.clone()),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::Number);
let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_bigint(&primitive)
}
Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")),
Expand Down Expand Up @@ -272,8 +272,8 @@ impl Interpreter {
Value::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")),
Value::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")),
Value::Object(_) => {
let prim_value = self.to_primitive(value, PreferredType::Number);
self.to_number(&prim_value)
let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_number(&primitive)
}
}
}
Expand All @@ -283,7 +283,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
pub fn to_numeric(&mut self, value: &Value) -> ResultValue {
let primitive = self.to_primitive(value, PreferredType::Number);
let primitive = self.to_primitive(value, PreferredType::Number)?;
if primitive.is_bigint() {
return Ok(primitive);
}
Expand All @@ -297,7 +297,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_numeric_number(&mut self, value: &Value) -> Result<f64, Value> {
let primitive = self.to_primitive(value, PreferredType::Number);
let primitive = self.to_primitive(value, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64());
}
Expand All @@ -324,55 +324,73 @@ impl Interpreter {
Err(())
}

/// <https://tc39.es/ecma262/#sec-ordinarytoprimitive>
pub(crate) fn ordinary_to_primitive(&mut self, o: &Value, hint: PreferredType) -> Value {
/// Converts an object to a primitive.
///
/// More information:
/// - [ECMAScript][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
pub(crate) fn ordinary_to_primitive(&mut self, o: &Value, hint: PreferredType) -> ResultValue {
// 1. Assert: Type(O) is Object.
debug_assert!(o.get_type() == Type::Object);
// 2. Assert: Type(hint) is String and its value is either "string" or "number".
debug_assert!(hint == PreferredType::String || hint == PreferredType::Number);

// 3. If hint is "string", then
// a. Let methodNames be « "toString", "valueOf" ».
// 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String {
["toString", "valueOf"]
} else {
["valueOf", "toString"]
};

// 5. For each name in methodNames in List order, do
for name in &method_names {
// a. Let method be ? Get(O, name).
let method: Value = o.get_field(*name);
// b. If IsCallable(method) is true, then
if method.is_function() {
let result = self.call(&method, &o, &[]);
match result {
Ok(val) => {
if val.is_object() {
// TODO: throw exception
continue;
} else {
return val;
}
}
Err(_) => continue,
// i. Let result be ? Call(method, O).
let result = self.call(&method, &o, &[])?;
// ii. If Type(result) is not Object, return result.
if !result.is_object() {
return Ok(result);
}
}
}

Value::undefined()
// 6. Throw a TypeError exception.
self.throw_type_error("cannot convert object to primitive value")
}

/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
///
/// <https://tc39.es/ecma262/#sec-toprimitive>
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_primitive(&mut self, input: &Value, preferred_type: PreferredType) -> Value {
let mut hint: PreferredType;
match input {
Value::Object(_) => {
hint = preferred_type;

// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
if hint == PreferredType::Default {
hint = PreferredType::Number;
};

self.ordinary_to_primitive(input, hint)
}
_ => input.clone(),
pub(crate) fn to_primitive(
&mut self,
input: &Value,
preferred_type: PreferredType,
) -> ResultValue {
// 1. Assert: input is an ECMAScript language value. (always a value not need to check)
// 2. If Type(input) is Object, then
if let Value::Object(_) = input {
let mut hint = preferred_type;

// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
// TODO: Add other steps.
if hint == PreferredType::Default {
hint = PreferredType::Number;
};

// g. Return ? OrdinaryToPrimitive(input, hint).
self.ordinary_to_primitive(input, hint)
} else {
// 3. Return input.
Ok(input.clone())
}
}

Expand All @@ -381,7 +399,7 @@ impl Interpreter {
/// https://tc39.es/ecma262/#sec-topropertykey
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_property_key(&mut self, value: &Value) -> ResultValue {
let key = self.to_primitive(value, PreferredType::String);
let key = self.to_primitive(value, PreferredType::String)?;
if key.is_symbol() {
Ok(key)
} else {
Expand Down
4 changes: 2 additions & 2 deletions boa/src/exec/operator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ impl Executable for BinOp {
let v_a = self.lhs().run(interpreter)?;
let v_b = 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::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(),
Expand Down

0 comments on commit c035f1a

Please sign in to comment.