quickjspp/value/
value.rs

1use std::collections::HashMap;
2use std::convert::TryFrom;
3use std::convert::TryInto;
4use std::hash::Hash;
5
6#[cfg(feature = "chrono")]
7use chrono::{DateTime, Utc};
8use libquickjspp_sys as q;
9
10#[cfg(feature = "bigint")]
11use crate::utils::create_bigint;
12#[cfg(feature = "chrono")]
13use crate::utils::create_date;
14use crate::utils::{
15    add_array_element, add_object_property, create_bool, create_empty_array, create_empty_object,
16    create_float, create_function, create_int, create_null, create_string,
17};
18use crate::{ExecutionError, ValueError};
19
20use super::tag::JsTag;
21use super::JsCompiledFunction;
22use super::JsFunction;
23use super::JsModule;
24use super::OwnedJsArray;
25use super::OwnedJsObject;
26
27/// OwnedJsValue wraps a Javascript value owned by the QuickJs runtime.
28///
29/// Guarantees cleanup of resources by dropping the value from the runtime.
30#[derive(PartialEq)]
31pub struct OwnedJsValue {
32    context: *mut q::JSContext,
33    // FIXME: make private again, just for testing
34    pub(crate) value: q::JSValue,
35}
36
37impl OwnedJsValue {
38    #[inline]
39    pub fn context(&self) -> *mut q::JSContext {
40        self.context
41    }
42
43    /// Create a new `OwnedJsValue` from a `JsValue`.
44    /// This will **NOT** increase the ref count of the underlying value. So
45    /// you have to manage memory yourself. Be careful when using this.
46    #[inline]
47    pub fn new(context: *mut q::JSContext, value: q::JSValue) -> Self {
48        Self { context, value }
49    }
50
51    /// Create a new `OwnedJsValue` from a `JsValue`.
52    /// This will increase the ref count of the underlying value.
53    #[inline]
54    pub fn own(context: *mut q::JSContext, value: &q::JSValue) -> Self {
55        unsafe { q::JS_DupValue(context, *value) };
56        Self::new(context, *value)
57    }
58
59    #[inline]
60    pub fn tag(&self) -> JsTag {
61        JsTag::from_c(&self.value)
62    }
63
64    /// Get the inner JSValue without increasing ref count.
65    ///
66    /// Unsafe because the caller must ensure proper memory management.
67    pub unsafe fn as_inner(&self) -> &q::JSValue {
68        &self.value
69    }
70
71    /// Extract the underlying JSValue.
72    ///
73    /// Unsafe because the caller must ensure memory management. (eg JS_FreeValue)
74    pub unsafe fn extract(self) -> q::JSValue {
75        let v = self.value;
76        std::mem::forget(self);
77        v
78    }
79
80    /// Replace the underlying JSValue.
81    /// This will decrease the ref count of the old value but remain the ref count of the new value.
82    pub fn replace(&mut self, new: q::JSValue) {
83        unsafe {
84            q::JS_FreeValue(self.context, self.value);
85        }
86        self.value = new;
87    }
88
89    /// Check if this value is `null`.
90    #[inline]
91    pub fn is_null(&self) -> bool {
92        self.tag().is_null()
93    }
94
95    /// Check if this value is `undefined`.
96    #[inline]
97    pub fn is_undefined(&self) -> bool {
98        self.tag() == JsTag::Undefined
99    }
100
101    /// Check if this value is `bool`.
102    #[inline]
103    pub fn is_bool(&self) -> bool {
104        self.tag() == JsTag::Bool
105    }
106
107    /// Check if this value is `int`.
108    #[inline]
109    pub fn is_int(&self) -> bool {
110        self.tag() == JsTag::Int
111    }
112
113    /// Check if this value is `float`.
114    #[inline]
115    pub fn is_float(&self) -> bool {
116        self.tag() == JsTag::Float64
117    }
118
119    /// Check if this value is a Javascript exception.
120    #[inline]
121    pub fn is_exception(&self) -> bool {
122        self.tag() == JsTag::Exception
123    }
124
125    /// Check if this value is a Javascript object.
126    #[inline]
127    pub fn is_object(&self) -> bool {
128        self.tag() == JsTag::Object
129    }
130
131    /// Check if this value is a Javascript array.
132    #[inline]
133    pub fn is_array(&self) -> bool {
134        unsafe { q::JS_IsArray(self.context, self.value) == 1 }
135    }
136
137    /// Check if this value is a Javascript function.
138    #[inline]
139    pub fn is_function(&self) -> bool {
140        unsafe { q::JS_IsFunction(self.context, self.value) == 1 }
141    }
142
143    /// Check if this value is a Javascript module.
144    #[inline]
145    pub fn is_module(&self) -> bool {
146        self.tag().is_module()
147    }
148
149    /// Check if this value is a Javascript string.
150    #[inline]
151    pub fn is_string(&self) -> bool {
152        self.tag() == JsTag::String
153    }
154
155    /// Check if this value is a bytecode compiled function.
156    #[inline]
157    pub fn is_compiled_function(&self) -> bool {
158        self.tag() == JsTag::FunctionBytecode
159    }
160
161    #[inline]
162    fn check_tag(&self, expected: JsTag) -> Result<(), ValueError> {
163        if self.tag() == expected {
164            Ok(())
165        } else {
166            Err(ValueError::UnexpectedType)
167        }
168    }
169
170    /// Convert this value into a bool
171    pub fn to_bool(&self) -> Result<bool, ValueError> {
172        self.check_tag(JsTag::Bool)?;
173        let val = unsafe { q::JS_VALUE_GET_BOOL(self.value) };
174        Ok(val)
175    }
176
177    /// Convert this value into an i32
178    pub fn to_int(&self) -> Result<i32, ValueError> {
179        self.check_tag(JsTag::Int)?;
180        let val = unsafe { q::JS_VALUE_GET_INT(self.value) };
181        Ok(val)
182    }
183
184    /// Convert this value into an f64
185    pub fn to_float(&self) -> Result<f64, ValueError> {
186        self.check_tag(JsTag::Float64)?;
187        let val = unsafe { q::JS_VALUE_GET_FLOAT64(self.value) };
188        Ok(val)
189    }
190
191    /// Convert this value into a string
192    pub fn to_string(&self) -> Result<String, ValueError> {
193        self.check_tag(JsTag::String)?;
194        let ptr = unsafe { q::JS_ToCStringLen2(self.context, std::ptr::null_mut(), self.value, 0) };
195
196        if ptr.is_null() {
197            return Err(ValueError::Internal(
198                "Could not convert string: got a null pointer".into(),
199            ));
200        }
201
202        let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
203
204        let s = cstr
205            .to_str()
206            .map_err(ValueError::InvalidString)?
207            .to_string();
208
209        // Free the c string.
210        unsafe { q::JS_FreeCString(self.context, ptr) };
211
212        Ok(s)
213    }
214
215    pub fn to_array(&self) -> Result<OwnedJsArray, ValueError> {
216        OwnedJsArray::try_from_value(self.clone())
217    }
218
219    /// Try convert this value into a object
220    pub fn try_into_object(self) -> Result<OwnedJsObject, ValueError> {
221        OwnedJsObject::try_from_value(self)
222    }
223
224    #[cfg(feature = "chrono")]
225    pub fn to_date(&self) -> Result<chrono::DateTime<chrono::Utc>, ValueError> {
226        use chrono::offset::TimeZone;
227
228        use crate::utils::js_date_constructor;
229
230        let date_constructor = js_date_constructor(self.context);
231        let is_date = unsafe { q::JS_IsInstanceOf(self.context, self.value, date_constructor) > 0 };
232
233        if is_date {
234            let getter = unsafe {
235                q::JS_GetPropertyStr(
236                    self.context,
237                    self.value,
238                    std::ffi::CStr::from_bytes_with_nul(b"getTime\0")
239                        .unwrap()
240                        .as_ptr(),
241                )
242            };
243            let tag = unsafe { q::JS_ValueGetTag(getter) };
244            assert_eq!(tag, q::JS_TAG_OBJECT);
245
246            let timestamp_raw =
247                unsafe { q::JS_Call(self.context, getter, self.value, 0, std::ptr::null_mut()) };
248
249            unsafe {
250                q::JS_FreeValue(self.context, getter);
251                q::JS_FreeValue(self.context, date_constructor);
252            };
253
254            let tag = unsafe { q::JS_ValueGetTag(timestamp_raw) };
255            let res = if tag == q::JS_TAG_FLOAT64 {
256                let f = unsafe { q::JS_VALUE_GET_FLOAT64(timestamp_raw) } as i64;
257                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
258                Ok(datetime)
259            } else if tag == q::JS_TAG_INT {
260                let f = unsafe { q::JS_VALUE_GET_INT(timestamp_raw) } as i64;
261                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
262                Ok(datetime)
263            } else {
264                Err(ValueError::Internal(
265                    "Could not convert 'Date' instance to timestamp".into(),
266                ))
267            };
268            return res;
269        } else {
270            unsafe { q::JS_FreeValue(self.context, date_constructor) };
271            Err(ValueError::UnexpectedType)
272        }
273    }
274
275    #[cfg(feature = "bigint")]
276    pub fn to_bigint(&self) -> Result<crate::BigInt, ValueError> {
277        use crate::value::BigInt;
278        use crate::value::BigIntOrI64;
279
280        let mut int: i64 = 0;
281        let ret = unsafe { q::JS_ToBigInt64(self.context, &mut int, self.value) };
282        if ret == 0 {
283            Ok(BigInt {
284                inner: BigIntOrI64::Int(int),
285            })
286        } else {
287            let ptr =
288                unsafe { q::JS_ToCStringLen2(self.context, std::ptr::null_mut(), self.value, 0) };
289
290            if ptr.is_null() {
291                return Err(ValueError::Internal(
292                    "Could not convert BigInt to string: got a null pointer".into(),
293                ));
294            }
295
296            let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
297            let bigint = num_bigint::BigInt::parse_bytes(cstr.to_bytes(), 10).unwrap();
298
299            // Free the c string.
300            unsafe { q::JS_FreeCString(self.context, ptr) };
301
302            Ok(BigInt {
303                inner: BigIntOrI64::BigInt(bigint),
304            })
305        }
306    }
307
308    /// Try convert this value into a function
309    pub fn try_into_function(self) -> Result<JsFunction, ValueError> {
310        JsFunction::try_from_value(self)
311    }
312
313    /// Try convert this value into a compiled function
314    pub fn try_into_compiled_function(self) -> Result<JsCompiledFunction, ValueError> {
315        JsCompiledFunction::try_from_value(self)
316    }
317
318    /// Try convert this value into a module
319    pub fn try_into_module(self) -> Result<JsModule, ValueError> {
320        JsModule::try_from_value(self)
321    }
322
323    /// Call the Javascript `.toString()` method on this value.
324    pub fn js_to_string(&self) -> Result<String, ExecutionError> {
325        let value = if self.is_string() {
326            self.to_string()?
327        } else {
328            let raw = unsafe { q::JS_ToString(self.context, self.value) };
329            let value = OwnedJsValue::new(self.context, raw);
330
331            if !value.is_string() {
332                return Err(ExecutionError::Internal(
333                    "Could not convert value to string".into(),
334                ));
335            }
336            value.to_string()?
337        };
338
339        Ok(value)
340    }
341
342    /// Call the Javascript `JSON.stringify()` method on this value.
343    pub fn to_json_string(&self, space: u8) -> Result<String, ExecutionError> {
344        let replacer = unsafe { q::JS_NewSpecialValue(q::JS_TAG_NULL, 0) };
345        let space = unsafe { q::JS_NewInt32(self.context, space as i32) };
346        let raw = unsafe { q::JS_JSONStringify(self.context, self.value, replacer, space) };
347
348        let value = OwnedJsValue::new(self.context, raw);
349
350        unsafe {
351            q::JS_FreeValue(self.context, replacer);
352            q::JS_FreeValue(self.context, space);
353        }
354
355        if !value.is_string() {
356            return Err(ExecutionError::Internal(
357                "Could not convert value to string".to_string(),
358            ));
359        }
360
361        let value = value.to_string()?;
362
363        Ok(value)
364    }
365
366    #[cfg(test)]
367    pub(crate) fn get_ref_count(&self) -> i32 {
368        let tag = unsafe { q::JS_ValueGetTag(self.value) };
369        if tag >= 8 {
370            // This transmute is OK since if tag < 0, the union will be a refcount
371            // pointer.
372            let ptr = unsafe { q::JS_VALUE_GET_PTR(self.value) as *mut q::JSRefCountHeader };
373            let pref: &mut q::JSRefCountHeader = &mut unsafe { *ptr };
374            pref.ref_count
375        } else {
376            -1
377        }
378    }
379}
380
381impl Drop for OwnedJsValue {
382    fn drop(&mut self) {
383        unsafe {
384            q::JS_FreeValue(self.context, self.value);
385        }
386    }
387}
388
389impl Clone for OwnedJsValue {
390    fn clone(&self) -> Self {
391        unsafe { q::JS_DupValue(self.context, self.value) };
392        Self {
393            context: self.context,
394            value: self.value,
395        }
396    }
397}
398
399impl std::fmt::Debug for OwnedJsValue {
400    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
401        write!(f, "{:?}(_)", self.tag())
402    }
403}
404
405impl TryFrom<OwnedJsValue> for bool {
406    type Error = ValueError;
407
408    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
409        value.to_bool()
410    }
411}
412
413impl TryFrom<OwnedJsValue> for i32 {
414    type Error = ValueError;
415
416    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
417        value.to_int()
418    }
419}
420
421impl TryFrom<OwnedJsValue> for f64 {
422    type Error = ValueError;
423
424    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
425        value.to_float()
426    }
427}
428
429impl TryFrom<OwnedJsValue> for String {
430    type Error = ValueError;
431
432    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
433        value.to_string()
434    }
435}
436
437#[cfg(feature = "chrono")]
438impl TryFrom<OwnedJsValue> for DateTime<Utc> {
439    type Error = ValueError;
440
441    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
442        value.to_date()
443    }
444}
445
446#[cfg(feature = "bigint")]
447impl TryFrom<OwnedJsValue> for crate::BigInt {
448    type Error = ValueError;
449
450    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
451        value.to_bigint()
452    }
453}
454
455#[cfg(feature = "bigint")]
456impl TryFrom<OwnedJsValue> for i64 {
457    type Error = ValueError;
458
459    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
460        value.to_bigint().map(|v| v.as_i64().unwrap())
461    }
462}
463
464#[cfg(feature = "bigint")]
465impl TryFrom<OwnedJsValue> for u64 {
466    type Error = ValueError;
467
468    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
469        use num_traits::ToPrimitive;
470        let bigint = value.to_bigint()?;
471        bigint
472            .into_bigint()
473            .to_u64()
474            .ok_or(ValueError::BigIntOverflow)
475    }
476}
477
478#[cfg(feature = "bigint")]
479impl TryFrom<OwnedJsValue> for i128 {
480    type Error = ValueError;
481
482    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
483        use num_traits::ToPrimitive;
484        let bigint = value.to_bigint()?;
485        bigint
486            .into_bigint()
487            .to_i128()
488            .ok_or(ValueError::BigIntOverflow)
489    }
490}
491
492#[cfg(feature = "bigint")]
493impl TryFrom<OwnedJsValue> for u128 {
494    type Error = ValueError;
495
496    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
497        use num_traits::ToPrimitive;
498        let bigint = value.to_bigint()?;
499        bigint
500            .into_bigint()
501            .to_u128()
502            .ok_or(ValueError::BigIntOverflow)
503    }
504}
505
506#[cfg(feature = "bigint")]
507impl TryFrom<OwnedJsValue> for num_bigint::BigInt {
508    type Error = ValueError;
509
510    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
511        value.to_bigint().map(|v| v.into_bigint())
512    }
513}
514
515impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Vec<T> {
516    type Error = ValueError;
517
518    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
519        let arr = value.to_array()?;
520        let mut ret: Vec<T> = vec![];
521        for i in 0..arr.length() {
522            let item = arr.get_index(i as u32).unwrap();
523            if let Some(item) = item {
524                let item = item.try_into()?;
525                ret.push(item);
526            }
527        }
528        Ok(ret)
529    }
530}
531
532impl<K: From<String> + PartialEq + Eq + Hash, V: TryFrom<OwnedJsValue, Error = ValueError>>
533    TryFrom<OwnedJsValue> for HashMap<K, V>
534{
535    type Error = ValueError;
536
537    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
538        let obj = value.try_into_object()?;
539        let mut ret: HashMap<K, V> = HashMap::new();
540        let mut iter = obj.properties_iter()?;
541        while let Some(Ok(key)) = iter.next() {
542            let key = key.to_string()?;
543            let item = obj.property(&key).unwrap();
544            if let Some(item) = item {
545                let item = item.try_into()?;
546                ret.insert(key.into(), item);
547            }
548        }
549        Ok(ret)
550    }
551}
552
553impl TryFrom<OwnedJsValue> for JsFunction {
554    type Error = ValueError;
555
556    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
557        JsFunction::try_from_value(value)
558    }
559}
560
561impl TryFrom<OwnedJsValue> for OwnedJsArray {
562    type Error = ValueError;
563
564    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
565        OwnedJsArray::try_from_value(value)
566    }
567}
568
569impl TryFrom<OwnedJsValue> for OwnedJsObject {
570    type Error = ValueError;
571
572    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
573        OwnedJsObject::try_from_value(value)
574    }
575}
576
577impl TryFrom<OwnedJsValue> for JsCompiledFunction {
578    type Error = ValueError;
579
580    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
581        JsCompiledFunction::try_from_value(value)
582    }
583}
584
585impl TryFrom<OwnedJsValue> for JsModule {
586    type Error = ValueError;
587
588    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
589        JsModule::try_from_value(value)
590    }
591}
592
593/// to avoid infinite recursion, we need to implement a ToOwnedJsValue trait for T,
594/// and then implement the `From<(*mut q::JSContext, T)>` trait for T and XXX<T> where T: ToOwnedJsValue
595///
596/// This trait should not be public, use the `From<(*mut q::JSContext, T)>` trait outside of this module.
597pub trait ToOwnedJsValue {
598    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue;
599}
600
601impl ToOwnedJsValue for bool {
602    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
603        let val = create_bool(context, self);
604        OwnedJsValue::new(context, val)
605    }
606}
607
608impl ToOwnedJsValue for i32 {
609    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
610        let val = create_int(context, self);
611        OwnedJsValue::new(context, val)
612    }
613}
614
615impl ToOwnedJsValue for i8 {
616    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
617        let val = create_int(context, self as i32);
618        OwnedJsValue::new(context, val)
619    }
620}
621
622impl ToOwnedJsValue for i16 {
623    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
624        let val = create_int(context, self as i32);
625        OwnedJsValue::new(context, val)
626    }
627}
628
629impl ToOwnedJsValue for u8 {
630    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
631        let val = create_int(context, self as i32);
632        OwnedJsValue::new(context, val)
633    }
634}
635
636impl ToOwnedJsValue for u16 {
637    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
638        let val = create_int(context, self as i32);
639        OwnedJsValue::new(context, val)
640    }
641}
642
643impl ToOwnedJsValue for f64 {
644    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
645        let val = create_float(context, self);
646        OwnedJsValue::new(context, val)
647    }
648}
649
650impl ToOwnedJsValue for u32 {
651    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
652        let val = create_float(context, self as f64);
653        OwnedJsValue::new(context, val)
654    }
655}
656
657impl ToOwnedJsValue for &str {
658    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
659        let val = create_string(context, self).unwrap();
660        OwnedJsValue::new(context, val)
661    }
662}
663
664impl ToOwnedJsValue for String {
665    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
666        let val = create_string(context, &self).unwrap();
667        OwnedJsValue::new(context, val)
668    }
669}
670
671#[cfg(feature = "chrono")]
672impl ToOwnedJsValue for DateTime<Utc> {
673    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
674        let val = create_date(context, self).unwrap();
675        OwnedJsValue::new(context, val)
676    }
677}
678
679#[cfg(feature = "bigint")]
680impl ToOwnedJsValue for crate::BigInt {
681    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
682        let val = create_bigint(context, self).unwrap();
683        OwnedJsValue::new(context, val)
684    }
685}
686
687#[cfg(feature = "bigint")]
688impl ToOwnedJsValue for num_bigint::BigInt {
689    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
690        let val = create_bigint(context, self.into()).unwrap();
691        OwnedJsValue::new(context, val)
692    }
693}
694
695#[cfg(feature = "bigint")]
696impl ToOwnedJsValue for i64 {
697    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
698        let val = create_bigint(context, self.into()).unwrap();
699        OwnedJsValue::new(context, val)
700    }
701}
702
703#[cfg(feature = "bigint")]
704impl ToOwnedJsValue for u64 {
705    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
706        let bigint: num_bigint::BigInt = self.into();
707        let val = create_bigint(context, bigint.into()).unwrap();
708        OwnedJsValue::new(context, val)
709    }
710}
711
712#[cfg(feature = "bigint")]
713impl ToOwnedJsValue for i128 {
714    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
715        let bigint: num_bigint::BigInt = self.into();
716        let val = create_bigint(context, bigint.into()).unwrap();
717        OwnedJsValue::new(context, val)
718    }
719}
720
721#[cfg(feature = "bigint")]
722impl ToOwnedJsValue for u128 {
723    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
724        let bigint: num_bigint::BigInt = self.into();
725        let val = create_bigint(context, bigint.into()).unwrap();
726        OwnedJsValue::new(context, val)
727    }
728}
729
730impl ToOwnedJsValue for JsFunction {
731    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
732        let val = create_function(context, self).unwrap();
733        OwnedJsValue::new(context, val)
734    }
735}
736
737/// for some cases like HashMap<String, OwnedJsValue>
738impl ToOwnedJsValue for OwnedJsValue {
739    fn to_owned(self, _: *mut q::JSContext) -> OwnedJsValue {
740        self
741    }
742}
743
744impl<T> ToOwnedJsValue for Vec<T>
745where
746    T: ToOwnedJsValue,
747{
748    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
749        let arr = create_empty_array(context).unwrap();
750        let _ = self.into_iter().enumerate().for_each(|(idx, val)| {
751            let val: OwnedJsValue = (context, val).into();
752            add_array_element(context, arr, idx as u32, unsafe { val.extract() }).unwrap();
753        });
754
755        OwnedJsValue::new(context, arr)
756    }
757}
758
759impl<K, V> ToOwnedJsValue for HashMap<K, V>
760where
761    K: Into<String>,
762    V: ToOwnedJsValue,
763{
764    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
765        let obj = create_empty_object(context).unwrap();
766        let _ = self.into_iter().for_each(|(key, val)| {
767            let val: OwnedJsValue = (context, val).into();
768            add_object_property(context, obj, key.into().as_str(), unsafe { val.extract() })
769                .unwrap();
770        });
771
772        OwnedJsValue::new(context, obj)
773    }
774}
775
776impl<T> ToOwnedJsValue for Option<T>
777where
778    T: ToOwnedJsValue,
779{
780    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
781        if let Some(val) = self {
782            (context, val).into()
783        } else {
784            OwnedJsValue::new(context, create_null())
785        }
786    }
787}
788
789impl<T> From<(*mut q::JSContext, T)> for OwnedJsValue
790where
791    T: ToOwnedJsValue,
792{
793    fn from((context, value): (*mut q::JSContext, T)) -> Self {
794        value.to_owned(context)
795    }
796}