quickjs_rusty/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 libquickjs_ng_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::OwnedJsPromise;
19use crate::{ExecutionError, ValueError};
20
21use super::tag::JsTag;
22use super::JsCompiledFunction;
23use super::JsFunction;
24use super::JsModule;
25use super::OwnedJsArray;
26use super::OwnedJsObject;
27
28/// OwnedJsValue wraps a Javascript value owned by the QuickJs runtime.
29///
30/// Guarantees cleanup of resources by dropping the value from the runtime.
31///
32/// **Safety**:
33///
34/// This type is `Send` and `Sync` only for convenience, since [OwnedJsValue](crate::OwnedJsValue)
35/// itself is just a wrapper around a raw pointer. But any operation on the underlying raw pointer is unsafe.
36/// Make sure using it in a same thread.
37///
38pub struct OwnedJsValue {
39    context: *mut q::JSContext,
40    // FIXME: make private again, just for testing
41    pub(crate) value: q::JSValue,
42}
43
44unsafe impl Send for OwnedJsValue {}
45unsafe impl Sync for OwnedJsValue {}
46
47impl PartialEq for OwnedJsValue {
48    fn eq(&self, other: &Self) -> bool {
49        unsafe { q::JS_Ext_GetPtr(self.value) == q::JS_Ext_GetPtr(other.value) }
50    }
51}
52
53impl OwnedJsValue {
54    #[inline]
55    pub fn context(&self) -> *mut q::JSContext {
56        self.context
57    }
58
59    /// Create a new `OwnedJsValue` from a `JsValue`.
60    /// This will **NOT** increase the ref count of the underlying value. So
61    /// you have to manage memory yourself. Be careful when using this.
62    #[inline]
63    pub fn new(context: *mut q::JSContext, value: q::JSValue) -> Self {
64        Self { context, value }
65    }
66
67    /// Create a new `OwnedJsValue` from a `JsValue`.
68    /// This will increase the ref count of the underlying value.
69    #[inline]
70    pub fn own(context: *mut q::JSContext, value: &q::JSValue) -> Self {
71        unsafe { q::JS_DupValue(context, *value) };
72        Self::new(context, *value)
73    }
74
75    #[inline]
76    pub fn tag(&self) -> JsTag {
77        JsTag::from_c(&self.value)
78    }
79
80    /// Get the inner JSValue without increasing ref count.
81    ///
82    /// Unsafe because the caller must ensure proper memory management.
83    pub unsafe fn as_inner(&self) -> &q::JSValue {
84        &self.value
85    }
86
87    /// Extract the underlying JSValue.
88    ///
89    /// Unsafe because the caller must ensure memory management. (eg JS_FreeValue)
90    pub unsafe fn extract(self) -> q::JSValue {
91        let v = self.value;
92        std::mem::forget(self);
93        v
94    }
95
96    /// Replace the underlying JSValue.
97    /// This will decrease the ref count of the old value but remain the ref count of the new value.
98    pub fn replace(&mut self, new: q::JSValue) {
99        unsafe {
100            q::JS_FreeValue(self.context, self.value);
101        }
102        self.value = new;
103    }
104
105    /// Check if this value is `null`.
106    #[inline]
107    pub fn is_null(&self) -> bool {
108        self.tag().is_null()
109    }
110
111    /// Check if this value is `undefined`.
112    #[inline]
113    pub fn is_undefined(&self) -> bool {
114        self.tag() == JsTag::Undefined
115    }
116
117    /// Check if this value is `bool`.
118    #[inline]
119    pub fn is_bool(&self) -> bool {
120        self.tag() == JsTag::Bool
121    }
122
123    /// Check if this value is `int`.
124    #[inline]
125    pub fn is_int(&self) -> bool {
126        self.tag() == JsTag::Int
127    }
128
129    /// Check if this value is `BigInt`.
130    #[inline]
131    #[cfg(feature = "bigint")]
132    pub fn is_bigint(&self) -> bool {
133        self.tag() == JsTag::BigInt || self.tag() == JsTag::ShortBigInt
134    }
135
136    /// Check if this value is `BigInt`, but short enough to fit in a i32
137    #[inline]
138    #[cfg(feature = "bigint")]
139    pub fn is_short_bigint(&self) -> bool {
140        self.tag() == JsTag::ShortBigInt
141    }
142
143    /// Check if this value is `float`.
144    #[inline]
145    pub fn is_float(&self) -> bool {
146        self.tag() == JsTag::Float64
147    }
148
149    /// Check if this value is a Javascript exception.
150    #[inline]
151    pub fn is_exception(&self) -> bool {
152        self.tag() == JsTag::Exception
153    }
154
155    /// Check if this value is a Javascript object.
156    #[inline]
157    pub fn is_object(&self) -> bool {
158        self.tag() == JsTag::Object
159    }
160
161    /// Check if this value is a Javascript array.
162    #[inline]
163    pub fn is_array(&self) -> bool {
164        unsafe { q::JS_IsArray(self.value) }
165    }
166
167    /// Check if this value is a Javascript array.
168    #[inline]
169    pub fn is_array_buffer(&self) -> bool {
170        unsafe { q::JS_IsArrayBuffer(self.value) }
171    }
172
173    /// Check if this value is a Javascript Proxy object.
174    #[inline]
175    pub fn is_proxy(&self) -> bool {
176        unsafe { q::JS_IsProxy(self.value) }
177    }
178
179    /// Check if this value is a Javascript function.
180    #[inline]
181    pub fn is_function(&self) -> bool {
182        unsafe { q::JS_IsFunction(self.context, self.value) }
183    }
184
185    /// Check if this value is a Javascript function.
186    #[inline]
187    pub fn is_constructor(&self) -> bool {
188        unsafe { q::JS_IsConstructor(self.context, self.value) }
189    }
190
191    /// Check if this value is a Javascript promise.
192    #[inline]
193    pub fn is_promise(&self) -> bool {
194        unsafe { q::JS_Ext_IsPromise(self.context, self.value) }
195    }
196
197    /// Check if this value is a Javascript error.
198    #[inline]
199    pub fn is_error(&self) -> bool {
200        unsafe { q::JS_IsError(self.context, self.value) }
201    }
202
203    /// Check if this value is a Javascript RegExp.
204    #[inline]
205    pub fn is_regexp(&self) -> bool {
206        unsafe { q::JS_IsRegExp(self.value) }
207    }
208
209    /// Check if this value is a Javascript Set.
210    #[inline]
211    pub fn is_set(&self) -> bool {
212        unsafe { q::JS_IsSet(self.value) }
213    }
214
215    /// Check if this value is a Javascript Map.
216    #[inline]
217    pub fn is_map(&self) -> bool {
218        unsafe { q::JS_IsMap(self.value) }
219    }
220
221    /// Check if this value is a Javascript WeakSet.
222    #[inline]
223    pub fn is_weak_set(&self) -> bool {
224        unsafe { q::JS_IsWeakSet(self.value) }
225    }
226
227    /// Check if this value is a Javascript WeakMap.
228    #[inline]
229    pub fn is_weak_map(&self) -> bool {
230        unsafe { q::JS_IsWeakMap(self.value) }
231    }
232
233    /// Check if this value is a Javascript WeakRef.
234    #[inline]
235    pub fn is_weak_ref(&self) -> bool {
236        unsafe { q::JS_IsWeakRef(self.value) }
237    }
238
239    /// Check if this value is a Javascript DataView.
240    #[inline]
241    pub fn is_data_view(&self) -> bool {
242        unsafe { q::JS_IsDataView(self.value) }
243    }
244
245    /// Check if this value is a Javascript module.
246    #[inline]
247    pub fn is_module(&self) -> bool {
248        self.tag().is_module()
249    }
250
251    /// Check if this value is a Javascript string.
252    #[inline]
253    pub fn is_string(&self) -> bool {
254        self.tag() == JsTag::String
255    }
256
257    /// Check if this value is a bytecode compiled function.
258    #[inline]
259    pub fn is_compiled_function(&self) -> bool {
260        self.tag() == JsTag::FunctionBytecode
261    }
262
263    #[inline]
264    fn check_tag(&self, expected: JsTag) -> Result<(), ValueError> {
265        if self.tag() == expected {
266            Ok(())
267        } else {
268            Err(ValueError::UnexpectedType)
269        }
270    }
271
272    /// Convert this value into a bool
273    pub fn to_bool(&self) -> Result<bool, ValueError> {
274        self.check_tag(JsTag::Bool)?;
275        let val = unsafe { q::JS_Ext_GetBool(self.value) };
276        Ok(val == 1)
277    }
278
279    /// Convert this value into an i32
280    pub fn to_int(&self) -> Result<i32, ValueError> {
281        self.check_tag(JsTag::Int)?;
282        let val = unsafe { q::JS_Ext_GetInt(self.value) };
283        Ok(val)
284    }
285
286    /// Convert this value into an f64
287    pub fn to_float(&self) -> Result<f64, ValueError> {
288        self.check_tag(JsTag::Float64)?;
289        let val = unsafe { q::JS_Ext_GetFloat64(self.value) };
290        Ok(val)
291    }
292
293    /// Convert this value into a string
294    pub fn to_string(&self) -> Result<String, ValueError> {
295        self.check_tag(JsTag::String)?;
296        let ptr =
297            unsafe { q::JS_ToCStringLen2(self.context, std::ptr::null_mut(), self.value, false) };
298
299        if ptr.is_null() {
300            return Err(ValueError::Internal(
301                "Could not convert string: got a null pointer".into(),
302            ));
303        }
304
305        let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) };
306
307        let s = cstr
308            .to_str()
309            .map_err(ValueError::InvalidString)?
310            .to_string();
311
312        // Free the c string.
313        unsafe { q::JS_FreeCString(self.context, ptr) };
314
315        Ok(s)
316    }
317
318    pub fn to_array(&self) -> Result<OwnedJsArray, ValueError> {
319        OwnedJsArray::try_from_value(self.clone())
320    }
321
322    pub fn get_proxy_target(&self, recursive: bool) -> Result<OwnedJsValue, ValueError> {
323        if !self.is_proxy() {
324            return Err(ValueError::UnexpectedType);
325        }
326
327        let target = unsafe { q::JS_GetProxyTarget(self.context, self.value) };
328        let target = OwnedJsValue::new(self.context, target);
329
330        if recursive && target.is_proxy() {
331            target.get_proxy_target(true)
332        } else {
333            Ok(target)
334        }
335    }
336
337    /// Try convert this value into a object
338    pub fn try_into_object(self) -> Result<OwnedJsObject, ValueError> {
339        OwnedJsObject::try_from_value(self)
340    }
341
342    #[cfg(feature = "chrono")]
343    pub fn to_date(&self) -> Result<chrono::DateTime<chrono::Utc>, ValueError> {
344        use chrono::offset::TimeZone;
345
346        use crate::utils::js_date_constructor;
347
348        let date_constructor = js_date_constructor(self.context);
349        let is_date = unsafe { q::JS_IsInstanceOf(self.context, self.value, date_constructor) > 0 };
350
351        if is_date {
352            let getter = unsafe {
353                q::JS_GetPropertyStr(
354                    self.context,
355                    self.value,
356                    std::ffi::CStr::from_bytes_with_nul(b"getTime\0")
357                        .unwrap()
358                        .as_ptr(),
359                )
360            };
361            let tag = unsafe { q::JS_Ext_ValueGetTag(getter) };
362            assert_eq!(tag, q::JS_TAG_OBJECT);
363
364            let timestamp_raw =
365                unsafe { q::JS_Call(self.context, getter, self.value, 0, std::ptr::null_mut()) };
366
367            unsafe {
368                q::JS_FreeValue(self.context, getter);
369                q::JS_FreeValue(self.context, date_constructor);
370            };
371
372            let tag = unsafe { q::JS_Ext_ValueGetTag(timestamp_raw) };
373            if tag == q::JS_TAG_FLOAT64 {
374                let f = unsafe { q::JS_Ext_GetFloat64(timestamp_raw) } as i64;
375                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
376                Ok(datetime)
377            } else if tag == q::JS_TAG_INT {
378                let f = unsafe { q::JS_Ext_GetInt(timestamp_raw) } as i64;
379                let datetime = chrono::Utc.timestamp_millis_opt(f).unwrap();
380                Ok(datetime)
381            } else {
382                Err(ValueError::Internal(
383                    "Could not convert 'Date' instance to timestamp".into(),
384                ))
385            }
386        } else {
387            unsafe { q::JS_FreeValue(self.context, date_constructor) };
388            Err(ValueError::UnexpectedType)
389        }
390    }
391
392    #[cfg(feature = "bigint")]
393    pub fn to_bigint(&self) -> Result<crate::BigInt, ValueError> {
394        use crate::value::BigInt;
395        use crate::value::BigIntOrI64;
396
397        if self.is_int() {
398            let int = self.to_int()?;
399            return Ok(BigInt {
400                inner: BigIntOrI64::Int(int as i64),
401            });
402        }
403
404        // numbers between 2^53 -1 and i64::MAX is treated as float in quickjs
405        if self.is_float() {
406            let float = self.to_float()?;
407            return Ok(BigInt {
408                inner: BigIntOrI64::Int(float as i64),
409            });
410        }
411
412        if self.is_short_bigint() {
413            let int = unsafe { q::JS_Ext_GetShortBigInt(self.value) };
414            return Ok(BigInt {
415                inner: BigIntOrI64::Int(int as i64),
416            });
417        }
418
419        let ret = unsafe { q::JS_Ext_BigIntToString1(self.context, self.value, 16) };
420        let ret = OwnedJsValue::new(self.context, ret);
421
422        if ret.is_exception() {
423            let err = OwnedJsValue::new(self.context, unsafe { q::JS_GetException(self.context) });
424
425            return Err(ValueError::Internal(format!(
426                "Could not convert BigInt to string: {}",
427                err.js_to_string().unwrap()
428            )));
429        }
430
431        if !ret.is_string() {
432            return Err(ValueError::Internal(
433                "Could not convert BigInt: unexpected error".into(),
434            ));
435        }
436
437        let ret_str = ret.to_string().unwrap();
438
439        let bigint = num_bigint::BigInt::parse_bytes(ret_str.as_bytes(), 16).unwrap();
440
441        Ok(BigInt {
442            inner: BigIntOrI64::BigInt(bigint),
443        })
444        // }
445    }
446
447    /// Try convert this value into a function
448    pub fn try_into_function(self) -> Result<JsFunction, ValueError> {
449        JsFunction::try_from_value(self)
450    }
451
452    /// Try convert this value into a function
453    pub fn try_into_promise(self) -> Result<OwnedJsPromise, ValueError> {
454        OwnedJsPromise::try_from_value(self)
455    }
456
457    /// Try convert this value into a compiled function
458    pub fn try_into_compiled_function(self) -> Result<JsCompiledFunction, ValueError> {
459        JsCompiledFunction::try_from_value(self)
460    }
461
462    /// Try convert this value into a module
463    pub fn try_into_module(self) -> Result<JsModule, ValueError> {
464        JsModule::try_from_value(self)
465    }
466
467    /// Call the Javascript `.toString()` method on this value.
468    pub fn js_to_string(&self) -> Result<String, ExecutionError> {
469        let value = if self.is_string() {
470            self.to_string()?
471        } else {
472            let raw = unsafe { q::JS_ToString(self.context, self.value) };
473            let value = OwnedJsValue::new(self.context, raw);
474
475            if !value.is_string() {
476                return Err(ExecutionError::Internal(
477                    "Could not convert value to string".into(),
478                ));
479            }
480            value.to_string()?
481        };
482
483        Ok(value)
484    }
485
486    /// Call the Javascript `JSON.stringify()` method on this value.
487    pub fn to_json_string(&self, space: u8) -> Result<String, ExecutionError> {
488        let replacer = unsafe { q::JS_Ext_NewSpecialValue(q::JS_TAG_NULL, 0) };
489        let space = unsafe { q::JS_Ext_NewInt32(self.context, space as i32) };
490        let raw = unsafe { q::JS_JSONStringify(self.context, self.value, replacer, space) };
491
492        let value = OwnedJsValue::new(self.context, raw);
493
494        unsafe {
495            q::JS_FreeValue(self.context, replacer);
496            q::JS_FreeValue(self.context, space);
497        }
498
499        if !value.is_string() {
500            return Err(ExecutionError::Internal(
501                "Could not convert value to string".to_string(),
502            ));
503        }
504
505        let value = value.to_string()?;
506
507        Ok(value)
508    }
509
510    #[cfg(test)]
511    pub(crate) fn get_ref_count(&self) -> i32 {
512        unsafe { q::JS_Ext_GetRefCount(self.value) }
513    }
514}
515
516impl Drop for OwnedJsValue {
517    fn drop(&mut self) {
518        unsafe {
519            q::JS_FreeValue(self.context, self.value);
520        }
521    }
522}
523
524impl Clone for OwnedJsValue {
525    fn clone(&self) -> Self {
526        unsafe { q::JS_DupValue(self.context, self.value) };
527        Self {
528            context: self.context,
529            value: self.value,
530        }
531    }
532}
533
534impl std::fmt::Debug for OwnedJsValue {
535    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
536        write!(f, "{:?}(_)", self.tag())
537    }
538}
539
540impl TryFrom<OwnedJsValue> for bool {
541    type Error = ValueError;
542
543    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
544        value.to_bool()
545    }
546}
547
548impl TryFrom<OwnedJsValue> for i32 {
549    type Error = ValueError;
550
551    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
552        if value.is_int() {
553            return value.to_int();
554        } else if value.is_float() {
555            let f = value.to_float()?;
556            if f.fract() != 0.0 {
557                return Err(ValueError::UnexpectedType);
558            }
559            if f < (i32::MIN as f64) || f > (i32::MAX as f64) {
560                return Err(ValueError::OutOfRange);
561            }
562            return Ok(f as i32);
563        }
564        Err(ValueError::UnexpectedType)
565    }
566}
567
568impl TryFrom<OwnedJsValue> for f64 {
569    type Error = ValueError;
570
571    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
572        if value.is_float() {
573            return value.to_float();
574        } else if value.is_int() {
575            let i = value.to_int()?;
576            return Ok(i as f64);
577        }
578        Err(ValueError::UnexpectedType)
579    }
580}
581
582impl TryFrom<OwnedJsValue> for String {
583    type Error = ValueError;
584
585    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
586        value.to_string()
587    }
588}
589
590#[cfg(feature = "chrono")]
591impl TryFrom<OwnedJsValue> for DateTime<Utc> {
592    type Error = ValueError;
593
594    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
595        value.to_date()
596    }
597}
598
599#[cfg(feature = "bigint")]
600impl TryFrom<OwnedJsValue> for crate::BigInt {
601    type Error = ValueError;
602
603    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
604        value.to_bigint()
605    }
606}
607
608#[cfg(feature = "bigint")]
609impl TryFrom<OwnedJsValue> for i64 {
610    type Error = ValueError;
611
612    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
613        if value.is_int() {
614            value.to_int().map(|v| v as i64)
615        } else {
616            value
617                .to_bigint()
618                .and_then(|v| v.as_i64().ok_or(ValueError::BigIntOverflow))
619        }
620    }
621}
622
623#[cfg(feature = "bigint")]
624impl TryFrom<OwnedJsValue> for u64 {
625    type Error = ValueError;
626
627    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
628        use num_traits::ToPrimitive;
629        let bigint = value.to_bigint()?;
630        bigint
631            .into_bigint()
632            .to_u64()
633            .ok_or(ValueError::BigIntOverflow)
634    }
635}
636
637#[cfg(feature = "bigint")]
638impl TryFrom<OwnedJsValue> for i128 {
639    type Error = ValueError;
640
641    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
642        use num_traits::ToPrimitive;
643        let bigint = value.to_bigint()?;
644        bigint
645            .into_bigint()
646            .to_i128()
647            .ok_or(ValueError::BigIntOverflow)
648    }
649}
650
651#[cfg(feature = "bigint")]
652impl TryFrom<OwnedJsValue> for u128 {
653    type Error = ValueError;
654
655    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
656        use num_traits::ToPrimitive;
657        let bigint = value.to_bigint()?;
658        bigint
659            .into_bigint()
660            .to_u128()
661            .ok_or(ValueError::BigIntOverflow)
662    }
663}
664
665#[cfg(feature = "bigint")]
666impl TryFrom<OwnedJsValue> for num_bigint::BigInt {
667    type Error = ValueError;
668
669    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
670        value.to_bigint().map(|v| v.into_bigint())
671    }
672}
673
674impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Option<T> {
675    type Error = ValueError;
676
677    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
678        if value.is_null() {
679            return Ok(None);
680        }
681        Ok(Some(value.try_into()?))
682    }
683}
684
685impl<T: TryFrom<OwnedJsValue, Error = ValueError>> TryFrom<OwnedJsValue> for Vec<T> {
686    type Error = ValueError;
687
688    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
689        let arr = value.to_array()?;
690        let mut ret: Vec<T> = vec![];
691        for i in 0..arr.length() {
692            let item = arr.get_index(i as u32).unwrap();
693            if let Some(item) = item {
694                let item = item.try_into()?;
695                ret.push(item);
696            }
697        }
698        Ok(ret)
699    }
700}
701
702impl<K: From<String> + PartialEq + Eq + Hash, V: TryFrom<OwnedJsValue, Error = ValueError>>
703    TryFrom<OwnedJsValue> for HashMap<K, V>
704{
705    type Error = ValueError;
706
707    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
708        let obj = value.try_into_object()?;
709        let mut ret: HashMap<K, V> = HashMap::new();
710        let mut iter = obj.properties_iter()?.step_by(2);
711        while let Some(Ok(key)) = iter.next() {
712            let key = key.to_string()?;
713            let item = obj.property(&key).unwrap();
714            if let Some(item) = item {
715                let item = item.try_into()?;
716                ret.insert(key.into(), item);
717            }
718        }
719        Ok(ret)
720    }
721}
722
723impl TryFrom<OwnedJsValue> for JsFunction {
724    type Error = ValueError;
725
726    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
727        JsFunction::try_from_value(value)
728    }
729}
730
731impl TryFrom<OwnedJsValue> for OwnedJsPromise {
732    type Error = ValueError;
733
734    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
735        OwnedJsPromise::try_from_value(value)
736    }
737}
738
739impl TryFrom<OwnedJsValue> for OwnedJsArray {
740    type Error = ValueError;
741
742    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
743        OwnedJsArray::try_from_value(value)
744    }
745}
746
747impl TryFrom<OwnedJsValue> for OwnedJsObject {
748    type Error = ValueError;
749
750    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
751        OwnedJsObject::try_from_value(value)
752    }
753}
754
755impl TryFrom<OwnedJsValue> for JsCompiledFunction {
756    type Error = ValueError;
757
758    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
759        JsCompiledFunction::try_from_value(value)
760    }
761}
762
763impl TryFrom<OwnedJsValue> for JsModule {
764    type Error = ValueError;
765
766    fn try_from(value: OwnedJsValue) -> Result<Self, Self::Error> {
767        JsModule::try_from_value(value)
768    }
769}
770
771/// to avoid infinite recursion, we need to implement a ToOwnedJsValue trait for T,
772/// and then implement the `From<(*mut q::JSContext, T)>` trait for T and XXX<T> where T: ToOwnedJsValue
773///
774/// This trait should not be public, use the `From<(*mut q::JSContext, T)>` trait outside of this module.
775pub trait ToOwnedJsValue {
776    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue;
777}
778
779impl ToOwnedJsValue for bool {
780    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
781        let val = create_bool(context, self);
782        OwnedJsValue::new(context, val)
783    }
784}
785
786impl ToOwnedJsValue for i32 {
787    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
788        let val = create_int(context, self);
789        OwnedJsValue::new(context, val)
790    }
791}
792
793impl ToOwnedJsValue for i8 {
794    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
795        let val = create_int(context, self as i32);
796        OwnedJsValue::new(context, val)
797    }
798}
799
800impl ToOwnedJsValue for i16 {
801    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
802        let val = create_int(context, self as i32);
803        OwnedJsValue::new(context, val)
804    }
805}
806
807impl ToOwnedJsValue for u8 {
808    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
809        let val = create_int(context, self as i32);
810        OwnedJsValue::new(context, val)
811    }
812}
813
814impl ToOwnedJsValue for u16 {
815    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
816        let val = create_int(context, self as i32);
817        OwnedJsValue::new(context, val)
818    }
819}
820
821impl ToOwnedJsValue for f64 {
822    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
823        let val = create_float(context, self);
824        OwnedJsValue::new(context, val)
825    }
826}
827
828impl ToOwnedJsValue for u32 {
829    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
830        let val = create_float(context, self as f64);
831        OwnedJsValue::new(context, val)
832    }
833}
834
835impl ToOwnedJsValue for &str {
836    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
837        let val = create_string(context, self).unwrap();
838        OwnedJsValue::new(context, val)
839    }
840}
841
842impl ToOwnedJsValue for String {
843    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
844        let val = create_string(context, &self).unwrap();
845        OwnedJsValue::new(context, val)
846    }
847}
848
849#[cfg(feature = "chrono")]
850impl ToOwnedJsValue for DateTime<Utc> {
851    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
852        let val = create_date(context, self).unwrap();
853        OwnedJsValue::new(context, val)
854    }
855}
856
857#[cfg(feature = "bigint")]
858impl ToOwnedJsValue for crate::BigInt {
859    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
860        let val = create_bigint(context, self).unwrap();
861        OwnedJsValue::new(context, val)
862    }
863}
864
865#[cfg(feature = "bigint")]
866impl ToOwnedJsValue for num_bigint::BigInt {
867    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
868        let val = create_bigint(context, self.into()).unwrap();
869        OwnedJsValue::new(context, val)
870    }
871}
872
873#[cfg(feature = "bigint")]
874impl ToOwnedJsValue for i64 {
875    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
876        let val = create_bigint(context, self.into()).unwrap();
877        OwnedJsValue::new(context, val)
878    }
879}
880
881#[cfg(feature = "bigint")]
882impl ToOwnedJsValue for u64 {
883    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
884        let bigint: num_bigint::BigInt = self.into();
885        let val = create_bigint(context, bigint.into()).unwrap();
886        OwnedJsValue::new(context, val)
887    }
888}
889
890#[cfg(feature = "bigint")]
891impl ToOwnedJsValue for i128 {
892    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
893        let bigint: num_bigint::BigInt = self.into();
894        let val = create_bigint(context, bigint.into()).unwrap();
895        OwnedJsValue::new(context, val)
896    }
897}
898
899#[cfg(feature = "bigint")]
900impl ToOwnedJsValue for u128 {
901    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
902        let bigint: num_bigint::BigInt = self.into();
903        let val = create_bigint(context, bigint.into()).unwrap();
904        OwnedJsValue::new(context, val)
905    }
906}
907
908impl ToOwnedJsValue for JsFunction {
909    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
910        let val = create_function(context, self).unwrap();
911        OwnedJsValue::new(context, val)
912    }
913}
914
915impl ToOwnedJsValue for OwnedJsPromise {
916    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
917        let val = unsafe { self.into_value().extract() };
918        OwnedJsValue::new(context, val)
919    }
920}
921
922/// for some cases like HashMap<String, OwnedJsValue>
923impl ToOwnedJsValue for OwnedJsValue {
924    fn to_owned(self, _: *mut q::JSContext) -> OwnedJsValue {
925        self
926    }
927}
928
929impl<T> ToOwnedJsValue for Vec<T>
930where
931    T: ToOwnedJsValue,
932{
933    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
934        let arr = create_empty_array(context).unwrap();
935        self.into_iter().enumerate().for_each(|(idx, val)| {
936            let val: OwnedJsValue = (context, val).into();
937            add_array_element(context, arr, idx as u32, unsafe { val.extract() }).unwrap();
938        });
939
940        OwnedJsValue::new(context, arr)
941    }
942}
943
944impl<K, V> ToOwnedJsValue for HashMap<K, V>
945where
946    K: Into<String>,
947    V: ToOwnedJsValue,
948{
949    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
950        let obj = create_empty_object(context).unwrap();
951        self.into_iter().for_each(|(key, val)| {
952            let val: OwnedJsValue = (context, val).into();
953            add_object_property(context, obj, key.into().as_str(), unsafe { val.extract() })
954                .unwrap();
955        });
956
957        OwnedJsValue::new(context, obj)
958    }
959}
960
961impl<T> ToOwnedJsValue for Option<T>
962where
963    T: ToOwnedJsValue,
964{
965    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
966        if let Some(val) = self {
967            (context, val).into()
968        } else {
969            OwnedJsValue::new(context, create_null())
970        }
971    }
972}
973
974impl<T> ToOwnedJsValue for &T
975where
976    T: ToOwnedJsValue,
977{
978    fn to_owned(self, context: *mut q::JSContext) -> OwnedJsValue {
979        (context, self).into()
980    }
981}
982
983impl<T> From<(*mut q::JSContext, T)> for OwnedJsValue
984where
985    T: ToOwnedJsValue,
986{
987    fn from((context, value): (*mut q::JSContext, T)) -> Self {
988        value.to_owned(context)
989    }
990}