boa/value/
operations.rs

1use super::*;
2use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number};
3
4impl JsValue {
5    #[inline]
6    pub fn add(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
7        Ok(match (self, other) {
8            // Fast path:
9            (Self::Integer(x), Self::Integer(y)) => Self::new(f64::from(*x) + f64::from(*y)),
10            (Self::Rational(x), Self::Rational(y)) => Self::new(x + y),
11            (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) + y),
12            (Self::Rational(x), Self::Integer(y)) => Self::new(x + f64::from(*y)),
13
14            (Self::String(ref x), Self::String(ref y)) => Self::from(JsString::concat(x, y)),
15            (Self::String(ref x), y) => Self::from(JsString::concat(x, y.to_string(context)?)),
16            (x, Self::String(ref y)) => Self::from(JsString::concat(x.to_string(context)?, y)),
17            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::add(x, y)),
18
19            // Slow path:
20            (_, _) => match (
21                self.to_primitive(context, PreferredType::Default)?,
22                other.to_primitive(context, PreferredType::Default)?,
23            ) {
24                (Self::String(ref x), ref y) => {
25                    Self::from(JsString::concat(x, y.to_string(context)?))
26                }
27                (ref x, Self::String(ref y)) => {
28                    Self::from(JsString::concat(x.to_string(context)?, y))
29                }
30                (x, y) => match (x.to_numeric(context)?, y.to_numeric(context)?) {
31                    (Numeric::Number(x), Numeric::Number(y)) => Self::new(x + y),
32                    (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
33                        Self::new(JsBigInt::add(x, y))
34                    }
35                    (_, _) => {
36                        return context.throw_type_error(
37                            "cannot mix BigInt and other types, use explicit conversions",
38                        )
39                    }
40                },
41            },
42        })
43    }
44
45    #[inline]
46    pub fn sub(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
47        Ok(match (self, other) {
48            // Fast path:
49            (Self::Integer(x), Self::Integer(y)) => Self::new(f64::from(*x) - f64::from(*y)),
50            (Self::Rational(x), Self::Rational(y)) => Self::new(x - y),
51            (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) - y),
52            (Self::Rational(x), Self::Integer(y)) => Self::new(x - f64::from(*y)),
53
54            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::sub(x, y)),
55
56            // Slow path:
57            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
58                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a - b),
59                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::sub(x, y)),
60                (_, _) => {
61                    return context.throw_type_error(
62                        "cannot mix BigInt and other types, use explicit conversions",
63                    );
64                }
65            },
66        })
67    }
68
69    #[inline]
70    pub fn mul(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
71        Ok(match (self, other) {
72            // Fast path:
73            (Self::Integer(x), Self::Integer(y)) => Self::new(f64::from(*x) * f64::from(*y)),
74            (Self::Rational(x), Self::Rational(y)) => Self::new(x * y),
75            (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) * y),
76            (Self::Rational(x), Self::Integer(y)) => Self::new(x * f64::from(*y)),
77
78            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::mul(x, y)),
79
80            // Slow path:
81            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
82                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a * b),
83                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::mul(x, y)),
84                (_, _) => {
85                    return context.throw_type_error(
86                        "cannot mix BigInt and other types, use explicit conversions",
87                    );
88                }
89            },
90        })
91    }
92
93    #[inline]
94    pub fn div(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
95        Ok(match (self, other) {
96            // Fast path:
97            (Self::Integer(x), Self::Integer(y)) => Self::new(f64::from(*x) / f64::from(*y)),
98            (Self::Rational(x), Self::Rational(y)) => Self::new(x / y),
99            (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x) / y),
100            (Self::Rational(x), Self::Integer(y)) => Self::new(x / f64::from(*y)),
101
102            (Self::BigInt(ref x), Self::BigInt(ref y)) => {
103                if y.is_zero() {
104                    return context.throw_range_error("BigInt division by zero");
105                }
106                Self::new(JsBigInt::div(x, y))
107            }
108
109            // Slow path:
110            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
111                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a / b),
112                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
113                    if y.is_zero() {
114                        return context.throw_range_error("BigInt division by zero");
115                    }
116                    Self::new(JsBigInt::div(x, y))
117                }
118                (_, _) => {
119                    return context.throw_type_error(
120                        "cannot mix BigInt and other types, use explicit conversions",
121                    );
122                }
123            },
124        })
125    }
126
127    #[inline]
128    pub fn rem(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
129        Ok(match (self, other) {
130            // Fast path:
131            (Self::Integer(x), Self::Integer(y)) => {
132                if *y == 0 {
133                    Self::nan()
134                } else {
135                    match x % *y {
136                        rem if rem == 0 && *x < 0 => Self::new(-0.0),
137                        rem => Self::new(rem),
138                    }
139                }
140            }
141            (Self::Rational(x), Self::Rational(y)) => Self::new((x % y).copysign(*x)),
142            (Self::Integer(x), Self::Rational(y)) => {
143                let x = f64::from(*x);
144                Self::new((x % y).copysign(x))
145            }
146
147            (Self::Rational(x), Self::Integer(y)) => Self::new((x % f64::from(*y)).copysign(*x)),
148
149            (Self::BigInt(ref x), Self::BigInt(ref y)) => {
150                if y.is_zero() {
151                    return context.throw_range_error("BigInt division by zero");
152                }
153                Self::new(JsBigInt::rem(x, y))
154            }
155
156            // Slow path:
157            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
158                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a % b),
159                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
160                    if y.is_zero() {
161                        return context.throw_range_error("BigInt division by zero");
162                    }
163                    Self::new(JsBigInt::rem(x, y))
164                }
165                (_, _) => {
166                    return context.throw_type_error(
167                        "cannot mix BigInt and other types, use explicit conversions",
168                    );
169                }
170            },
171        })
172    }
173
174    #[inline]
175    pub fn pow(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
176        Ok(match (self, other) {
177            // Fast path:
178            (Self::Integer(x), Self::Integer(y)) => Self::new(f64::from(*x).powi(*y)),
179            (Self::Rational(x), Self::Rational(y)) => Self::new(x.powf(*y)),
180            (Self::Integer(x), Self::Rational(y)) => Self::new(f64::from(*x).powf(*y)),
181            (Self::Rational(x), Self::Integer(y)) => Self::new(x.powi(*y)),
182
183            (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b, context)?),
184
185            // Slow path:
186            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
187                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a.powf(b)),
188                (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => {
189                    Self::new(JsBigInt::pow(a, b, context)?)
190                }
191                (_, _) => {
192                    return context.throw_type_error(
193                        "cannot mix BigInt and other types, use explicit conversions",
194                    );
195                }
196            },
197        })
198    }
199
200    #[inline]
201    pub fn bitand(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
202        Ok(match (self, other) {
203            // Fast path:
204            (Self::Integer(x), Self::Integer(y)) => Self::new(x & y),
205            (Self::Rational(x), Self::Rational(y)) => {
206                Self::new(f64_to_int32(*x) & f64_to_int32(*y))
207            }
208            (Self::Integer(x), Self::Rational(y)) => Self::new(x & f64_to_int32(*y)),
209            (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) & y),
210
211            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitand(x, y)),
212
213            // Slow path:
214            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
215                (Numeric::Number(a), Numeric::Number(b)) => {
216                    Self::new(f64_to_int32(a) & f64_to_int32(b))
217                }
218                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
219                    Self::new(JsBigInt::bitand(x, y))
220                }
221                (_, _) => {
222                    return context.throw_type_error(
223                        "cannot mix BigInt and other types, use explicit conversions",
224                    );
225                }
226            },
227        })
228    }
229
230    #[inline]
231    pub fn bitor(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
232        Ok(match (self, other) {
233            // Fast path:
234            (Self::Integer(x), Self::Integer(y)) => Self::new(x | y),
235            (Self::Rational(x), Self::Rational(y)) => {
236                Self::new(f64_to_int32(*x) | f64_to_int32(*y))
237            }
238            (Self::Integer(x), Self::Rational(y)) => Self::new(x | f64_to_int32(*y)),
239            (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) | y),
240
241            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitor(x, y)),
242
243            // Slow path:
244            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
245                (Numeric::Number(a), Numeric::Number(b)) => {
246                    Self::new(f64_to_int32(a) | f64_to_int32(b))
247                }
248                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
249                    Self::new(JsBigInt::bitor(x, y))
250                }
251                (_, _) => {
252                    return context.throw_type_error(
253                        "cannot mix BigInt and other types, use explicit conversions",
254                    );
255                }
256            },
257        })
258    }
259
260    #[inline]
261    pub fn bitxor(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
262        Ok(match (self, other) {
263            // Fast path:
264            (Self::Integer(x), Self::Integer(y)) => Self::new(x ^ y),
265            (Self::Rational(x), Self::Rational(y)) => {
266                Self::new(f64_to_int32(*x) ^ f64_to_int32(*y))
267            }
268            (Self::Integer(x), Self::Rational(y)) => Self::new(x ^ f64_to_int32(*y)),
269            (Self::Rational(x), Self::Integer(y)) => Self::new(f64_to_int32(*x) ^ y),
270
271            (Self::BigInt(ref x), Self::BigInt(ref y)) => Self::new(JsBigInt::bitxor(x, y)),
272
273            // Slow path:
274            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
275                (Numeric::Number(a), Numeric::Number(b)) => {
276                    Self::new(f64_to_int32(a) ^ f64_to_int32(b))
277                }
278                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
279                    Self::new(JsBigInt::bitxor(x, y))
280                }
281                (_, _) => {
282                    return context.throw_type_error(
283                        "cannot mix BigInt and other types, use explicit conversions",
284                    );
285                }
286            },
287        })
288    }
289
290    #[inline]
291    pub fn shl(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
292        Ok(match (self, other) {
293            // Fast path:
294            (Self::Integer(x), Self::Integer(y)) => Self::new(x.wrapping_shl(*y as u32)),
295            (Self::Rational(x), Self::Rational(y)) => {
296                Self::new(f64_to_int32(*x).wrapping_shl(f64_to_uint32(*y)))
297            }
298            (Self::Integer(x), Self::Rational(y)) => Self::new(x.wrapping_shl(f64_to_uint32(*y))),
299            (Self::Rational(x), Self::Integer(y)) => {
300                Self::new(f64_to_int32(*x).wrapping_shl(*y as u32))
301            }
302
303            (Self::BigInt(ref a), Self::BigInt(ref b)) => {
304                Self::new(JsBigInt::shift_left(a, b, context)?)
305            }
306
307            // Slow path:
308            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
309                (Numeric::Number(x), Numeric::Number(y)) => {
310                    Self::new(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))
311                }
312                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
313                    Self::new(JsBigInt::shift_left(x, y, context)?)
314                }
315                (_, _) => {
316                    return context.throw_type_error(
317                        "cannot mix BigInt and other types, use explicit conversions",
318                    );
319                }
320            },
321        })
322    }
323
324    #[inline]
325    pub fn shr(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
326        Ok(match (self, other) {
327            // Fast path:
328            (Self::Integer(x), Self::Integer(y)) => Self::new(x.wrapping_shr(*y as u32)),
329            (Self::Rational(x), Self::Rational(y)) => {
330                Self::new(f64_to_int32(*x).wrapping_shr(f64_to_uint32(*y)))
331            }
332            (Self::Integer(x), Self::Rational(y)) => Self::new(x.wrapping_shr(f64_to_uint32(*y))),
333            (Self::Rational(x), Self::Integer(y)) => {
334                Self::new(f64_to_int32(*x).wrapping_shr(*y as u32))
335            }
336
337            (Self::BigInt(ref a), Self::BigInt(ref b)) => {
338                Self::new(JsBigInt::shift_right(a, b, context)?)
339            }
340
341            // Slow path:
342            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
343                (Numeric::Number(x), Numeric::Number(y)) => {
344                    Self::new(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))
345                }
346                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {
347                    Self::new(JsBigInt::shift_right(x, y, context)?)
348                }
349                (_, _) => {
350                    return context.throw_type_error(
351                        "cannot mix BigInt and other types, use explicit conversions",
352                    );
353                }
354            },
355        })
356    }
357
358    #[inline]
359    pub fn ushr(&self, other: &Self, context: &mut Context) -> JsResult<JsValue> {
360        Ok(match (self, other) {
361            // Fast path:
362            (Self::Integer(x), Self::Integer(y)) => Self::new((*x as u32).wrapping_shr(*y as u32)),
363            (Self::Rational(x), Self::Rational(y)) => {
364                Self::new(f64_to_uint32(*x).wrapping_shr(f64_to_uint32(*y)))
365            }
366            (Self::Integer(x), Self::Rational(y)) => {
367                Self::new((*x as u32).wrapping_shr(f64_to_uint32(*y)))
368            }
369            (Self::Rational(x), Self::Integer(y)) => {
370                Self::new(f64_to_uint32(*x).wrapping_shr(*y as u32))
371            }
372
373            // Slow path:
374            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {
375                (Numeric::Number(x), Numeric::Number(y)) => {
376                    Self::new(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y)))
377                }
378                (Numeric::BigInt(_), Numeric::BigInt(_)) => {
379                    return context
380                        .throw_type_error("BigInts have no unsigned right shift, use >> instead");
381                }
382                (_, _) => {
383                    return context.throw_type_error(
384                        "cannot mix BigInt and other types, use explicit conversions",
385                    );
386                }
387            },
388        })
389    }
390
391    #[inline]
392    pub fn neg(&self, context: &mut Context) -> JsResult<JsValue> {
393        Ok(match *self {
394            Self::Symbol(_) | Self::Undefined => Self::new(f64::NAN),
395            Self::Object(_) => Self::new(match self.to_numeric_number(context) {
396                Ok(num) => -num,
397                Err(_) => f64::NAN,
398            }),
399            Self::String(ref str) => Self::new(match f64::from_str(str) {
400                Ok(num) => -num,
401                Err(_) => f64::NAN,
402            }),
403            Self::Rational(num) => Self::new(-num),
404            Self::Integer(num) if num == 0 => Self::new(-f64::from(0)),
405            Self::Integer(num) => Self::new(-num),
406            Self::Boolean(true) => Self::new(1),
407            Self::Boolean(false) | Self::Null => Self::new(0),
408            Self::BigInt(ref x) => Self::new(JsBigInt::neg(x)),
409        })
410    }
411
412    #[inline]
413    pub fn not(&self, _: &mut Context) -> JsResult<bool> {
414        Ok(!self.to_boolean())
415    }
416
417    /// Abstract relational comparison
418    ///
419    /// The comparison `x < y`, where `x` and `y` are values, produces `true`, `false`,
420    /// or `undefined` (which indicates that at least one operand is `NaN`).
421    ///
422    /// In addition to `x` and `y` the algorithm takes a Boolean flag named `LeftFirst` as a parameter.
423    /// The flag is used to control the order in which operations with potentially visible side-effects
424    /// are performed upon `x` and `y`. It is necessary because ECMAScript specifies left to right evaluation
425    /// of expressions. The default value of LeftFirst is `true` and indicates that the `x` parameter
426    /// corresponds to an expression that occurs to the left of the `y` parameter's corresponding expression.
427    ///
428    /// If `LeftFirst` is `false`, the reverse is the case and operations must be performed upon `y` before `x`.
429    ///
430    /// More Information:
431    ///  - [ECMAScript reference][spec]
432    ///
433    /// [spec]: https://tc39.es/ecma262/#sec-abstract-relational-comparison
434    pub fn abstract_relation(
435        &self,
436        other: &Self,
437        left_first: bool,
438        context: &mut Context,
439    ) -> JsResult<AbstractRelation> {
440        Ok(match (self, other) {
441            // Fast path (for some common operations):
442            (Self::Integer(x), Self::Integer(y)) => (x < y).into(),
443            (Self::Integer(x), Self::Rational(y)) => Number::less_than(f64::from(*x), *y),
444            (Self::Rational(x), Self::Integer(y)) => Number::less_than(*x, f64::from(*y)),
445            (Self::Rational(x), Self::Rational(y)) => Number::less_than(*x, *y),
446            (Self::BigInt(ref x), Self::BigInt(ref y)) => (x < y).into(),
447
448            // Slow path:
449            (_, _) => {
450                let (px, py) = if left_first {
451                    let px = self.to_primitive(context, PreferredType::Number)?;
452                    let py = other.to_primitive(context, PreferredType::Number)?;
453                    (px, py)
454                } else {
455                    // NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
456                    let py = other.to_primitive(context, PreferredType::Number)?;
457                    let px = self.to_primitive(context, PreferredType::Number)?;
458                    (px, py)
459                };
460
461                match (px, py) {
462                    (Self::String(ref x), Self::String(ref y)) => {
463                        if x.starts_with(y.as_str()) {
464                            return Ok(AbstractRelation::False);
465                        }
466                        if y.starts_with(x.as_str()) {
467                            return Ok(AbstractRelation::True);
468                        }
469                        for (x, y) in x.chars().zip(y.chars()) {
470                            if x != y {
471                                return Ok((x < y).into());
472                            }
473                        }
474                        unreachable!()
475                    }
476                    (Self::BigInt(ref x), Self::String(ref y)) => {
477                        if let Some(y) = JsBigInt::from_string(y) {
478                            (*x < y).into()
479                        } else {
480                            AbstractRelation::Undefined
481                        }
482                    }
483                    (Self::String(ref x), Self::BigInt(ref y)) => {
484                        if let Some(x) = JsBigInt::from_string(x) {
485                            (x < *y).into()
486                        } else {
487                            AbstractRelation::Undefined
488                        }
489                    }
490                    (px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) {
491                        (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y),
492                        (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(),
493                        (Numeric::BigInt(ref x), Numeric::Number(y)) => {
494                            if y.is_nan() {
495                                return Ok(AbstractRelation::Undefined);
496                            }
497                            if y.is_infinite() {
498                                return Ok(y.is_sign_positive().into());
499                            }
500                            let n = if y.is_sign_negative() {
501                                y.floor()
502                            } else {
503                                y.ceil()
504                            };
505                            (*x < JsBigInt::try_from(n).unwrap()).into()
506                        }
507                        (Numeric::Number(x), Numeric::BigInt(ref y)) => {
508                            if x.is_nan() {
509                                return Ok(AbstractRelation::Undefined);
510                            }
511                            if x.is_infinite() {
512                                return Ok(x.is_sign_negative().into());
513                            }
514                            let n = if x.is_sign_negative() {
515                                x.floor()
516                            } else {
517                                x.ceil()
518                            };
519                            (JsBigInt::try_from(n).unwrap() < *y).into()
520                        }
521                    },
522                }
523            }
524        })
525    }
526
527    /// The less than operator (`<`) returns `true` if the left operand is less than the right operand,
528    /// and `false` otherwise.
529    ///
530    /// More Information:
531    ///  - [MDN documentation][mdn]
532    ///  - [ECMAScript reference][spec]
533    ///
534    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than
535    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
536    #[inline]
537    pub fn lt(&self, other: &Self, context: &mut Context) -> JsResult<bool> {
538        match self.abstract_relation(other, true, context)? {
539            AbstractRelation::True => Ok(true),
540            AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
541        }
542    }
543
544    /// The less than or equal operator (`<=`) returns `true` if the left operand is less than
545    /// or equal to the right operand, and `false` otherwise.
546    ///
547    /// More Information:
548    ///  - [MDN documentation][mdn]
549    ///  - [ECMAScript reference][spec]
550    ///
551    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than_or_equal
552    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
553    #[inline]
554    pub fn le(&self, other: &Self, context: &mut Context) -> JsResult<bool> {
555        match other.abstract_relation(self, false, context)? {
556            AbstractRelation::False => Ok(true),
557            AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
558        }
559    }
560
561    /// The greater than operator (`>`) returns `true` if the left operand is greater than
562    /// the right operand, and `false` otherwise.
563    ///
564    /// More Information:
565    ///  - [MDN documentation][mdn]
566    ///  - [ECMAScript reference][spec]
567    ///
568    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than
569    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
570    #[inline]
571    pub fn gt(&self, other: &Self, context: &mut Context) -> JsResult<bool> {
572        match other.abstract_relation(self, false, context)? {
573            AbstractRelation::True => Ok(true),
574            AbstractRelation::False | AbstractRelation::Undefined => Ok(false),
575        }
576    }
577
578    /// The greater than or equal operator (`>=`) returns `true` if the left operand is greater than
579    /// or equal to the right operand, and `false` otherwise.
580    ///
581    /// More Information:
582    ///  - [MDN documentation][mdn]
583    ///  - [ECMAScript reference][spec]
584    ///
585    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than_or_equal
586    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation
587    #[inline]
588    pub fn ge(&self, other: &Self, context: &mut Context) -> JsResult<bool> {
589        match self.abstract_relation(other, true, context)? {
590            AbstractRelation::False => Ok(true),
591            AbstractRelation::True | AbstractRelation::Undefined => Ok(false),
592        }
593    }
594}
595
596/// The result of the [Abstract Relational Comparison][arc].
597///
598/// Comparison `x < y`, where `x` and `y` are values.
599/// It produces `true`, `false`, or `undefined`
600/// (which indicates that at least one operand is `NaN`).
601///
602/// [arc]: https://tc39.es/ecma262/#sec-abstract-relational-comparison
603#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
604pub enum AbstractRelation {
605    /// `x` is less than `y`
606    True,
607    /// `x` is **not** less than `y`
608    False,
609    /// Indicates that at least one operand is `NaN`
610    Undefined,
611}
612
613impl From<bool> for AbstractRelation {
614    #[inline]
615    fn from(value: bool) -> Self {
616        if value {
617            AbstractRelation::True
618        } else {
619            AbstractRelation::False
620        }
621    }
622}