Skip to main content

boa_engine/value/
variant.rs

1#[cfg(feature = "annex-b")]
2use crate::builtins::is_html_dda::IsHTMLDDA;
3use crate::{JsBigInt, JsObject, JsSymbol, JsValue};
4use boa_engine::js_string;
5use boa_string::JsString;
6
7/// A non-mutable variant of a `JsValue`.
8/// Represents either a primitive value ([`bool`], [`f64`], [`i32`]) or a reference
9/// to a heap allocated value ([`JsString`], [`JsSymbol`]).
10#[derive(Debug, PartialEq)]
11pub enum JsVariant {
12    /// `null` - A null value, for when a value doesn't exist.
13    Null,
14    /// `undefined` - An undefined value, for when a field or index doesn't exist.
15    Undefined,
16    /// `boolean` - A `true` / `false` value, for if a certain criteria is met.
17    Boolean(bool),
18    /// `String` - A UTF-16 string, such as `"Hello, world"`.
19    String(JsString),
20    /// `Number` - A 64-bit floating point number, such as `3.1415` or `Infinity`.
21    /// This is the default representation of a number. If a number can be represented
22    /// as an integer, it will be stored as an `Integer` variant instead.
23    Float64(f64),
24    /// `Number` - A 32-bit integer, such as `42`.
25    Integer32(i32),
26    /// `BigInt` - holds any arbitrary large signed integer.
27    BigInt(JsBigInt),
28    /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.
29    Object(JsObject),
30    /// `Symbol` - A Symbol Primitive type.
31    Symbol(JsSymbol),
32}
33
34impl From<JsVariant> for JsValue {
35    fn from(value: JsVariant) -> Self {
36        match value {
37            JsVariant::Null => JsValue::null(),
38            JsVariant::Undefined => JsValue::undefined(),
39            JsVariant::Boolean(b) => JsValue::new(b),
40            JsVariant::String(s) => JsValue::new(s),
41            JsVariant::Float64(f) => JsValue::new(f),
42            JsVariant::Integer32(i) => JsValue::new(i),
43            JsVariant::BigInt(b) => JsValue::new(b),
44            JsVariant::Object(o) => JsValue::new(o),
45            JsVariant::Symbol(s) => JsValue::new(s),
46        }
47    }
48}
49
50impl JsVariant {
51    /// Check if the variant is an `undefined` value.
52    #[inline]
53    #[must_use]
54    pub fn is_undefined(&self) -> bool {
55        matches!(self, JsVariant::Undefined)
56    }
57
58    /// `typeof` operator. Returns a string representing the type of the
59    /// given ECMA Value.
60    ///
61    /// More information:
62    /// - [ECMAScript reference][spec]
63    ///
64    /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator
65    #[must_use]
66    pub fn type_of(&self) -> &'static str {
67        match self {
68            JsVariant::Float64(_) | JsVariant::Integer32(_) => "number",
69            JsVariant::String(_) => "string",
70            JsVariant::Boolean(_) => "boolean",
71            JsVariant::Symbol(_) => "symbol",
72            JsVariant::Null => "object",
73            JsVariant::Undefined => "undefined",
74            JsVariant::BigInt(_) => "bigint",
75            JsVariant::Object(object) => {
76                #[cfg(feature = "annex-b")]
77                if object.is::<IsHTMLDDA>() {
78                    return "undefined";
79                }
80                if object.is_callable() {
81                    "function"
82                } else {
83                    "object"
84                }
85            }
86        }
87    }
88
89    /// Same as [`JsVariant::type_of`], but returning a [`JsString`] instead.
90    #[must_use]
91    pub fn js_type_of(&self) -> JsString {
92        match self {
93            JsVariant::Float64(_) | JsVariant::Integer32(_) => js_string!("number"),
94            JsVariant::String(_) => js_string!("string"),
95            JsVariant::Boolean(_) => js_string!("boolean"),
96            JsVariant::Symbol(_) => js_string!("symbol"),
97            JsVariant::Null => js_string!("object"),
98            JsVariant::Undefined => js_string!("undefined"),
99            JsVariant::BigInt(_) => js_string!("bigint"),
100            JsVariant::Object(object) => {
101                #[cfg(feature = "annex-b")]
102                if object.is::<IsHTMLDDA>() {
103                    return js_string!("undefined");
104                }
105                if object.is_callable() {
106                    js_string!("function")
107                } else {
108                    js_string!("object")
109                }
110            }
111        }
112    }
113}