boa/value/equality.rs
1use super::*;
2use crate::{builtins::Number, Context};
3
4impl JsValue {
5 /// Strict equality comparison.
6 ///
7 /// This method is executed when doing strict equality comparisons with the `===` operator.
8 /// For more information, check <https://tc39.es/ecma262/#sec-strict-equality-comparison>.
9 pub fn strict_equals(&self, other: &Self) -> bool {
10 // 1. If Type(x) is different from Type(y), return false.
11 if self.get_type() != other.get_type() {
12 return false;
13 }
14
15 match (self, other) {
16 // 2. If Type(x) is Number or BigInt, then
17 // a. Return ! Type(x)::equal(x, y).
18 (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::equal(x, y),
19 (Self::Rational(x), Self::Rational(y)) => Number::equal(*x, *y),
20 (Self::Rational(x), Self::Integer(y)) => Number::equal(*x, f64::from(*y)),
21 (Self::Integer(x), Self::Rational(y)) => Number::equal(f64::from(*x), *y),
22 (Self::Integer(x), Self::Integer(y)) => x == y,
23
24 //Null has to be handled specially because "typeof null" returns object and if we managed
25 //this without a special case we would compare self and other as if they were actually
26 //objects which unfortunately fails
27 //Specification Link: https://tc39.es/ecma262/#sec-typeof-operator
28 (Self::Null, Self::Null) => true,
29
30 // 3. Return ! SameValueNonNumeric(x, y).
31 (_, _) => Self::same_value_non_numeric(self, other),
32 }
33 }
34
35 /// Abstract equality comparison.
36 ///
37 /// This method is executed when doing abstract equality comparisons with the `==` operator.
38 /// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
39 #[allow(clippy::float_cmp)]
40 pub fn equals(&self, other: &Self, context: &mut Context) -> JsResult<bool> {
41 // 1. If Type(x) is the same as Type(y), then
42 // a. Return the result of performing Strict Equality Comparison x === y.
43 if self.get_type() == other.get_type() {
44 return Ok(self.strict_equals(other));
45 }
46
47 Ok(match (self, other) {
48 // 2. If x is null and y is undefined, return true.
49 // 3. If x is undefined and y is null, return true.
50 (Self::Null, Self::Undefined) | (Self::Undefined, Self::Null) => true,
51
52 // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
53 // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
54 //
55 // https://github.com/rust-lang/rust/issues/54883
56 (Self::Integer(_), Self::String(_))
57 | (Self::Rational(_), Self::String(_))
58 | (Self::String(_), Self::Integer(_))
59 | (Self::String(_), Self::Rational(_))
60 | (Self::Rational(_), Self::Boolean(_))
61 | (Self::Integer(_), Self::Boolean(_)) => {
62 let x = self.to_number(context)?;
63 let y = other.to_number(context)?;
64 Number::equal(x, y)
65 }
66
67 // 6. If Type(x) is BigInt and Type(y) is String, then
68 // a. Let n be ! StringToBigInt(y).
69 // b. If n is NaN, return false.
70 // c. Return the result of the comparison x == n.
71 (Self::BigInt(ref a), Self::String(ref b)) => match JsBigInt::from_string(b) {
72 Some(ref b) => a == b,
73 None => false,
74 },
75
76 // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.
77 (Self::String(ref a), Self::BigInt(ref b)) => match JsBigInt::from_string(a) {
78 Some(ref a) => a == b,
79 None => false,
80 },
81
82 // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
83 (Self::Boolean(x), _) => return other.equals(&JsValue::new(*x as i32), context),
84
85 // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
86 (_, Self::Boolean(y)) => return self.equals(&JsValue::new(*y as i32), context),
87
88 // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result
89 // of the comparison x == ? ToPrimitive(y).
90 (Self::Object(_), _) => {
91 let primitive = self.to_primitive(context, PreferredType::Default)?;
92 return primitive.equals(other, context);
93 }
94
95 // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result
96 // of the comparison ? ToPrimitive(x) == y.
97 (_, Self::Object(_)) => {
98 let primitive = other.to_primitive(context, PreferredType::Default)?;
99 return primitive.equals(self, context);
100 }
101
102 // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then
103 // a. If x or y are any of NaN, +∞, or -∞, return false.
104 // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false.
105 (Self::BigInt(ref a), Self::Rational(ref b)) => a == b,
106 (Self::Rational(ref a), Self::BigInt(ref b)) => a == b,
107 (Self::BigInt(ref a), Self::Integer(ref b)) => a == b,
108 (Self::Integer(ref a), Self::BigInt(ref b)) => a == b,
109
110 // 13. Return false.
111 _ => false,
112 })
113 }
114
115 /// The internal comparison abstract operation SameValue(x, y),
116 /// where x and y are ECMAScript language values, produces true or false.
117 ///
118 /// More information:
119 /// - [ECMAScript][spec]
120 ///
121 /// [spec]: https://tc39.es/ecma262/#sec-samevalue
122 pub fn same_value(x: &JsValue, y: &JsValue) -> bool {
123 // 1. If Type(x) is different from Type(y), return false.
124 if x.get_type() != y.get_type() {
125 return false;
126 }
127
128 match (x, y) {
129 // 2. If Type(x) is Number or BigInt, then
130 // a. Return ! Type(x)::SameValue(x, y).
131 (JsValue::BigInt(x), JsValue::BigInt(y)) => JsBigInt::same_value(x, y),
132 (JsValue::Rational(x), JsValue::Rational(y)) => Number::same_value(*x, *y),
133 (JsValue::Rational(x), JsValue::Integer(y)) => Number::same_value(*x, f64::from(*y)),
134 (JsValue::Integer(x), JsValue::Rational(y)) => Number::same_value(f64::from(*x), *y),
135 (JsValue::Integer(x), JsValue::Integer(y)) => x == y,
136
137 // 3. Return ! SameValueNonNumeric(x, y).
138 (_, _) => Self::same_value_non_numeric(x, y),
139 }
140 }
141
142 /// The internal comparison abstract operation `SameValueZero(x, y)`,
143 /// where `x` and `y` are ECMAScript language values, produces `true` or `false`.
144 ///
145 /// `SameValueZero` differs from SameValue only in its treatment of `+0` and `-0`.
146 ///
147 /// More information:
148 /// - [ECMAScript][spec]
149 ///
150 /// [spec]: https://tc39.es/ecma262/#sec-samevaluezero
151 pub fn same_value_zero(x: &JsValue, y: &JsValue) -> bool {
152 if x.get_type() != y.get_type() {
153 return false;
154 }
155
156 match (x, y) {
157 // 2. If Type(x) is Number or BigInt, then
158 // a. Return ! Type(x)::SameValueZero(x, y).
159 (JsValue::BigInt(x), JsValue::BigInt(y)) => JsBigInt::same_value_zero(x, y),
160
161 (JsValue::Rational(x), JsValue::Rational(y)) => Number::same_value_zero(*x, *y),
162 (JsValue::Rational(x), JsValue::Integer(y)) => {
163 Number::same_value_zero(*x, f64::from(*y))
164 }
165 (JsValue::Integer(x), JsValue::Rational(y)) => {
166 Number::same_value_zero(f64::from(*x), *y)
167 }
168 (JsValue::Integer(x), JsValue::Integer(y)) => x == y,
169
170 // 3. Return ! SameValueNonNumeric(x, y).
171 (_, _) => Self::same_value_non_numeric(x, y),
172 }
173 }
174
175 fn same_value_non_numeric(x: &JsValue, y: &JsValue) -> bool {
176 debug_assert!(x.get_type() == y.get_type());
177 match (x, y) {
178 (JsValue::Null, JsValue::Null) | (JsValue::Undefined, JsValue::Undefined) => true,
179 (JsValue::String(ref x), JsValue::String(ref y)) => x == y,
180 (JsValue::Boolean(x), JsValue::Boolean(y)) => x == y,
181 (JsValue::Object(ref x), JsValue::Object(ref y)) => JsObject::equals(x, y),
182 (JsValue::Symbol(ref x), JsValue::Symbol(ref y)) => x == y,
183 _ => false,
184 }
185 }
186}