quickjs_oxide/engine/api/core/
value.rs1use std::fmt;
5use std::rc::{Rc, Weak};
6
7use oxide_core::Handle;
8
9use super::RuntimeError;
10use super::runtime::Shared;
11use crate::engine::value::JsString;
12use crate::engine::value::bigint::JsBigInt;
13
14#[derive(Clone)]
16pub enum Value {
17 Undefined,
18 Null,
19 Bool(bool),
20 Int(i32),
21 Float(f64),
22 BigInt(JsBigInt),
23 String(JsString),
24 Symbol(SymbolRef),
25 Object(ObjectRef),
26}
27
28impl Value {
29 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
31 Ok(self.clone())
32 }
33
34 pub fn number(value: f64) -> Self {
37 if value.fract() == 0.0
38 && value >= f64::from(i32::MIN)
39 && value <= f64::from(i32::MAX)
40 && !(value == 0.0 && value.is_sign_negative())
41 {
42 Value::Int(value as i32)
43 } else {
44 Value::Float(value)
45 }
46 }
47
48 pub const fn as_number(&self) -> Option<f64> {
50 match self {
51 Value::Int(value) => Some(*value as f64),
52 Value::Float(value) => Some(*value),
53 _ => None,
54 }
55 }
56
57 pub fn as_object(&self) -> Option<&ObjectRef> {
58 match self {
59 Value::Object(object) => Some(object),
60 _ => None,
61 }
62 }
63
64 pub fn to_boolean(&self) -> bool {
66 match self {
67 Value::Undefined | Value::Null => false,
68 Value::Bool(value) => *value,
69 Value::Int(value) => *value != 0,
70 Value::Float(value) => *value != 0.0 && !value.is_nan(),
71 Value::BigInt(value) => !value.is_zero(),
72 Value::String(value) => !value.is_empty(),
73 Value::Symbol(_) | Value::Object(_) => true,
76 }
77 }
78
79 pub const fn type_of(&self) -> &'static str {
82 match self {
83 Value::Undefined => "undefined",
84 Value::Null | Value::Object(_) => "object",
85 Value::Bool(_) => "boolean",
86 Value::Int(_) | Value::Float(_) => "number",
87 Value::BigInt(_) => "bigint",
88 Value::String(_) => "string",
89 Value::Symbol(_) => "symbol",
90 }
91 }
92
93 pub fn strict_equal(&self, other: &Self) -> bool {
95 match (self.as_number(), other.as_number()) {
96 (Some(x), Some(y)) => x == y,
97 _ => self.same_value(other),
98 }
99 }
100
101 pub fn same_value(&self, other: &Self) -> bool {
103 match (self, other) {
104 (Value::Undefined, Value::Undefined) | (Value::Null, Value::Null) => true,
105 (Value::Bool(x), Value::Bool(y)) => x == y,
106 (Value::BigInt(x), Value::BigInt(y)) => x == y,
107 (Value::String(x), Value::String(y)) => x == y,
108 (Value::Symbol(x), Value::Symbol(y)) => x == y,
109 (Value::Object(x), Value::Object(y)) => x == y,
110 _ => match (self.as_number(), other.as_number()) {
111 (Some(x), Some(y)) => {
112 (x.is_nan() && y.is_nan())
113 || (x == y && x.is_sign_negative() == y.is_sign_negative())
114 }
115 _ => false,
116 },
117 }
118 }
119
120 pub fn same_value_zero(&self, other: &Self) -> bool {
122 match (self.as_number(), other.as_number()) {
123 (Some(x), Some(y)) => (x.is_nan() && y.is_nan()) || x == y,
124 _ => self.same_value(other),
125 }
126 }
127}
128
129impl PartialEq for Value {
132 fn eq(&self, other: &Self) -> bool {
133 match (self, other) {
134 (Value::Undefined, Value::Undefined) | (Value::Null, Value::Null) => true,
135 (Value::Bool(x), Value::Bool(y)) => x == y,
136 (Value::Int(x), Value::Int(y)) => x == y,
137 (Value::Float(x), Value::Float(y)) => x == y,
138 (Value::BigInt(x), Value::BigInt(y)) => x == y,
139 (Value::String(x), Value::String(y)) => x == y,
140 (Value::Symbol(x), Value::Symbol(y)) => x == y,
141 (Value::Object(x), Value::Object(y)) => x == y,
142 _ => false,
143 }
144 }
145}
146
147impl fmt::Debug for Value {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 match self {
150 Value::Undefined => f.write_str("Undefined"),
151 Value::Null => f.write_str("Null"),
152 Value::Bool(value) => write!(f, "Bool({value})"),
153 Value::Int(value) => write!(f, "Int({value})"),
154 Value::Float(value) => write!(f, "Float({value:?})"),
155 Value::BigInt(value) => write!(f, "BigInt({value})"),
156 Value::String(value) => write!(f, "String({:?})", value.to_utf8_lossy()),
157 Value::Symbol(value) => write!(f, "Symbol({value:?})"),
158 Value::Object(value) => write!(f, "Object({value:?})"),
159 }
160 }
161}
162
163impl From<bool> for Value {
164 fn from(value: bool) -> Self {
165 Value::Bool(value)
166 }
167}
168
169impl From<i32> for Value {
170 fn from(value: i32) -> Self {
171 Value::Int(value)
172 }
173}
174
175impl From<f64> for Value {
176 fn from(value: f64) -> Self {
177 Value::number(value)
178 }
179}
180
181impl From<JsString> for Value {
182 fn from(value: JsString) -> Self {
183 Value::String(value)
184 }
185}
186
187impl From<ObjectRef> for Value {
188 fn from(value: ObjectRef) -> Self {
189 Value::Object(value)
190 }
191}
192
193impl From<CallableRef> for Value {
194 fn from(value: CallableRef) -> Self {
195 Value::Object(value.0)
196 }
197}
198
199pub(crate) struct Slot {
204 pub(crate) shared: Weak<Shared>,
205 pub(crate) handle: Handle,
206 pub(crate) value: oxide_core::Value,
208}
209
210impl Drop for Slot {
211 fn drop(&mut self) {
212 if let Some(shared) = self.shared.upgrade() {
213 shared.release(self.handle);
214 }
215 }
216}
217
218impl Slot {
219 pub(crate) fn of(&self, shared: &Rc<Shared>) -> bool {
221 std::ptr::eq(self.shared.as_ptr(), Rc::as_ptr(shared))
222 }
223}
224
225#[derive(Clone)]
227pub struct ObjectRef {
228 pub(crate) slot: Rc<Slot>,
229}
230
231#[derive(Clone)]
233pub struct SymbolRef {
234 pub(crate) slot: Rc<Slot>,
235}
236
237#[derive(Clone, PartialEq, Debug)]
239pub struct CallableRef(pub(crate) ObjectRef);
240
241macro_rules! reference {
242 ($name:ident) => {
243 impl $name {
244 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
245 Ok(self.clone())
246 }
247
248 pub fn belongs_to(&self, runtime: &super::Runtime) -> bool {
250 self.slot.of(&runtime.shared)
251 }
252
253 pub(crate) fn core_value(&self) -> oxide_core::Value {
254 self.slot.value
255 }
256 }
257
258 impl PartialEq for $name {
259 fn eq(&self, other: &Self) -> bool {
260 Weak::ptr_eq(&self.slot.shared, &other.slot.shared)
261 && self.slot.value == other.slot.value
262 }
263 }
264
265 impl Eq for $name {}
266
267 impl fmt::Debug for $name {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(f, "{}({:?})", stringify!($name), self.slot.value)
270 }
271 }
272 };
273}
274
275reference!(ObjectRef);
276reference!(SymbolRef);
277
278impl CallableRef {
279 pub fn try_clone(&self) -> Result<Self, RuntimeError> {
280 Ok(self.clone())
281 }
282
283 pub fn as_object(&self) -> &ObjectRef {
284 &self.0
285 }
286
287 pub fn into_object(self) -> ObjectRef {
288 self.0
289 }
290
291 pub fn belongs_to(&self, runtime: &super::Runtime) -> bool {
292 self.0.belongs_to(runtime)
293 }
294}
295
296impl std::ops::Deref for CallableRef {
297 type Target = ObjectRef;
298
299 fn deref(&self) -> &ObjectRef {
300 &self.0
301 }
302}