Skip to main content

rquickjs_serde/
ser.rs

1use alloc::string::ToString as _;
2
3use rquickjs::{Array, Ctx, Object, String as JSString, Value, object::Property};
4use serde::{Serialize, ser};
5
6use crate::err::{Error, Result};
7
8/// `Serializer` is a serializer for [Value] values, implementing the `serde::Serializer` trait.
9///
10/// This struct is responsible for converting Rust types into [Value] using the Serde
11/// serialization framework.
12///
13/// ```
14/// # use rquickjs::{Runtime, Context, Value};
15/// # use rquickjs_serde::Serializer;
16/// # use serde::Serializer as _;
17/// #
18/// let rt = Runtime::new().unwrap();
19/// let ctx = Context::full(&rt).unwrap();
20/// ctx.with(|ctx| {
21///     let mut serializer = Serializer::from_context(ctx.clone()).unwrap();
22///     let value = serializer.serialize_u32(42).unwrap();
23///     assert!(value.is_number());
24/// });
25/// ```
26pub struct Serializer<'js> {
27    pub context: Ctx<'js>,
28}
29
30pub struct SeqSerializer<'se, 'js> {
31    ser: &'se mut Serializer<'js>,
32    value: Array<'js>,
33}
34
35pub struct TupleVariantSerializer<'se, 'js> {
36    ser: &'se mut Serializer<'js>,
37    wrapper: Object<'js>,
38    value: Array<'js>,
39}
40
41pub struct MapSerializer<'se, 'js> {
42    ser: &'se mut Serializer<'js>,
43    value: Object<'js>,
44    /** current map key, will be left `None` for structs */
45    key: Option<Value<'js>>,
46}
47
48pub struct StructVariantSerializer<'se, 'js> {
49    ser: &'se mut Serializer<'js>,
50    wrapper: Object<'js>,
51    value: Object<'js>,
52}
53
54impl<'se, 'js> SeqSerializer<'se, 'js> {
55    fn new(ser: &'se mut Serializer<'js>) -> Result<Self> {
56        let value = Array::new(ser.context.clone()).map_err(Error::new)?;
57
58        Ok(Self { ser, value })
59    }
60}
61
62impl<'se, 'js> TupleVariantSerializer<'se, 'js> {
63    fn new(ser: &'se mut Serializer<'js>, variant: &'static str) -> Result<Self> {
64        let wrapper = Object::new(ser.context.clone()).map_err(Error::new)?;
65        let value = Array::new(ser.context.clone()).map_err(Error::new)?;
66        wrapper.set(variant, value.clone()).map_err(Error::new)?;
67
68        Ok(Self {
69            ser,
70            wrapper,
71            value,
72        })
73    }
74}
75
76impl<'se, 'js> MapSerializer<'se, 'js> {
77    fn new(ser: &'se mut Serializer<'js>) -> Result<Self> {
78        let value = Object::new(ser.context.clone()).map_err(Error::new)?;
79        Ok(Self {
80            ser,
81            value,
82            key: None,
83        })
84    }
85}
86impl<'se, 'js> StructVariantSerializer<'se, 'js> {
87    fn new(ser: &'se mut Serializer<'js>, variant: &'static str) -> Result<Self> {
88        let wrapper = Object::new(ser.context.clone()).map_err(Error::new)?;
89        let value = Object::new(ser.context.clone()).map_err(Error::new)?;
90        wrapper.set(variant, value.clone()).map_err(Error::new)?;
91
92        Ok(Self {
93            ser,
94            wrapper,
95            value,
96        })
97    }
98}
99
100impl<'js> Serializer<'js> {
101    pub fn from_context(context: Ctx<'js>) -> Result<Self> {
102        Ok(Self {
103            context: context.clone(),
104        })
105    }
106}
107impl<'js, 'se> ser::Serializer for &'se mut Serializer<'js> {
108    type Ok = Value<'js>;
109    type Error = Error;
110
111    type SerializeSeq = SeqSerializer<'se, 'js>;
112    type SerializeTuple = SeqSerializer<'se, 'js>;
113    type SerializeTupleStruct = SeqSerializer<'se, 'js>;
114    type SerializeTupleVariant = TupleVariantSerializer<'se, 'js>;
115    type SerializeMap = MapSerializer<'se, 'js>;
116    type SerializeStruct = MapSerializer<'se, 'js>;
117    type SerializeStructVariant = StructVariantSerializer<'se, 'js>;
118
119    fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
120        self.serialize_i32(i32::from(v))
121    }
122
123    fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
124        self.serialize_i32(i32::from(v))
125    }
126
127    fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
128        Ok(Value::new_int(self.context.clone(), v))
129    }
130
131    fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
132        Ok(Value::new_number(self.context.clone(), v as _))
133    }
134
135    fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
136        self.serialize_i32(i32::from(v))
137    }
138
139    fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
140        self.serialize_i32(i32::from(v))
141    }
142
143    fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
144        // NOTE: See optimization note in serialize_f64.
145        self.serialize_f64(f64::from(v))
146    }
147
148    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
149        Ok(Value::new_number(self.context.clone(), v as _))
150    }
151
152    fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
153        // NOTE: See optimization note in serialize_f64.
154        self.serialize_f64(f64::from(v))
155    }
156
157    fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
158        // NOTE: QuickJS will create a number value backed by an i32 when the value is within
159        // the i32::MIN..=i32::MAX as an optimization. Otherwise the value will be backed by a f64.
160        Ok(Value::new_float(self.context.clone(), v))
161    }
162
163    fn serialize_bool(self, b: bool) -> Result<Self::Ok> {
164        Ok(Value::new_bool(self.context.clone(), b))
165    }
166
167    fn serialize_char(self, v: char) -> Result<Self::Ok> {
168        self.serialize_str(&v.to_string())
169    }
170
171    fn serialize_str(self, v: &str) -> Result<Self::Ok> {
172        let js_string = JSString::from_str(self.context.clone(), v).map_err(Error::new)?;
173        Ok(Value::from(js_string))
174    }
175
176    fn serialize_none(self) -> Result<Self::Ok> {
177        self.serialize_unit()
178    }
179
180    fn serialize_unit(self) -> Result<Self::Ok> {
181        Ok(Value::new_null(self.context.clone()))
182    }
183
184    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
185        self.serialize_unit()
186    }
187
188    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
189    where
190        T: ?Sized + Serialize,
191    {
192        value.serialize(self)
193    }
194
195    fn serialize_unit_variant(
196        self,
197        _name: &'static str,
198        _variant_index: u32,
199        variant: &'static str,
200    ) -> Result<Self::Ok> {
201        self.serialize_str(variant)
202    }
203
204    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
205    where
206        T: ?Sized + Serialize,
207    {
208        value.serialize(self)
209    }
210
211    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
212        SeqSerializer::new(self)
213    }
214
215    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
216        SeqSerializer::new(self)
217    }
218
219    fn serialize_tuple_struct(
220        self,
221        _name: &'static str,
222        len: usize,
223    ) -> Result<Self::SerializeTupleStruct> {
224        self.serialize_seq(Some(len))
225    }
226
227    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
228        MapSerializer::new(self)
229    }
230
231    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
232        MapSerializer::new(self)
233    }
234
235    fn serialize_struct_variant(
236        self,
237        _name: &'static str,
238        _variant_index: u32,
239        variant: &'static str,
240        _len: usize,
241    ) -> Result<Self::SerializeStructVariant> {
242        StructVariantSerializer::new(self, variant)
243    }
244
245    fn serialize_tuple_variant(
246        self,
247        _name: &'static str,
248        _variant_index: u32,
249        variant: &'static str,
250        _len: usize,
251    ) -> Result<Self::SerializeTupleVariant> {
252        TupleVariantSerializer::new(self, variant)
253    }
254
255    fn serialize_newtype_variant<T>(
256        self,
257        _name: &'static str,
258        _variant_index: u32,
259        variant: &'static str,
260        value: &T,
261    ) -> Result<Self::Ok>
262    where
263        T: ?Sized + Serialize,
264    {
265        let obj = Object::new(self.context.clone()).map_err(Error::new)?;
266        let value = value.serialize(&mut *self)?;
267        obj.set(variant, value).map_err(Error::new)?;
268        Ok(obj.into())
269    }
270
271    fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok> {
272        Err(Error::new("Cannot serialize bytes"))
273    }
274}
275
276impl<'se, 'js> ser::SerializeSeq for SeqSerializer<'se, 'js> {
277    type Ok = Value<'js>;
278    type Error = Error;
279
280    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
281    where
282        T: ?Sized + Serialize,
283    {
284        self.value
285            .set(self.value.len(), value.serialize(&mut *self.ser)?)
286            .map_err(Error::new)
287    }
288
289    fn end(self) -> Result<Self::Ok> {
290        Ok(self.value.into())
291    }
292}
293
294impl<'se, 'js> ser::SerializeTuple for SeqSerializer<'se, 'js> {
295    type Ok = Value<'js>;
296    type Error = Error;
297
298    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
299    where
300        T: ?Sized + Serialize,
301    {
302        self.value
303            .set(self.value.len(), value.serialize(&mut *self.ser)?)
304            .map_err(Error::new)
305    }
306
307    fn end(self) -> Result<Self::Ok> {
308        Ok(self.value.into())
309    }
310}
311
312impl<'se, 'js> ser::SerializeTupleStruct for SeqSerializer<'se, 'js> {
313    type Ok = Value<'js>;
314    type Error = Error;
315
316    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
317    where
318        T: ?Sized + Serialize,
319    {
320        self.value
321            .set(self.value.len(), value.serialize(&mut *self.ser)?)
322            .map_err(Error::new)
323    }
324
325    fn end(self) -> Result<Self::Ok> {
326        Ok(self.value.into())
327    }
328}
329
330impl<'se, 'js> ser::SerializeTupleVariant for TupleVariantSerializer<'se, 'js> {
331    type Ok = Value<'js>;
332    type Error = Error;
333
334    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
335    where
336        T: ?Sized + Serialize,
337    {
338        self.value
339            .set(self.value.len(), value.serialize(&mut *self.ser)?)
340            .map_err(Error::new)
341    }
342
343    fn end(self) -> Result<Self::Ok> {
344        Ok(self.wrapper.into())
345    }
346}
347
348impl<'se, 'js> ser::SerializeMap for MapSerializer<'se, 'js> {
349    type Ok = Value<'js>;
350    type Error = Error;
351
352    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
353    where
354        T: ?Sized + Serialize,
355    {
356        self.key = Some(key.serialize(&mut *self.ser)?);
357        Ok(())
358    }
359
360    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
361    where
362        T: ?Sized + Serialize,
363    {
364        let key = self
365            .key
366            .take()
367            .ok_or_else(|| Error::new("serialize_value before serialize_key"))?;
368
369        let value = value.serialize(&mut *self.ser)?;
370        let prop = Property::from(value).writable().configurable().enumerable();
371        self.value.prop::<_, _, _>(key, prop).map_err(Error::new)
372    }
373
374    fn end(self) -> Result<Self::Ok> {
375        Ok(self.value.into())
376    }
377}
378
379impl<'se, 'js> ser::SerializeStruct for MapSerializer<'se, 'js> {
380    type Ok = Value<'js>;
381    type Error = Error;
382
383    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
384    where
385        T: ?Sized + Serialize,
386    {
387        let value = value.serialize(&mut *self.ser)?;
388        self.value.set(key, value).map_err(Error::new)?;
389        Ok(())
390    }
391
392    fn end(self) -> Result<Self::Ok> {
393        Ok(self.value.into())
394    }
395}
396
397impl<'se, 'js> ser::SerializeStructVariant for StructVariantSerializer<'se, 'js> {
398    type Ok = Value<'js>;
399    type Error = Error;
400
401    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
402    where
403        T: ?Sized + Serialize,
404    {
405        let value = value.serialize(&mut *self.ser)?;
406        self.value.set(key, value).map_err(Error::new)?;
407        Ok(())
408    }
409
410    fn end(self) -> Result<Self::Ok> {
411        Ok(self.wrapper.into())
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use std::collections::BTreeMap;
418
419    use rquickjs::{Ctx, Function, Object, Value};
420    use serde::{Serialize, Serializer};
421
422    use super::Serializer as ValueSerializer;
423    use crate::err::Result;
424    use crate::test::Runtime;
425    use crate::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
426    use quickcheck::quickcheck;
427
428    fn with_serializer<F: FnMut(&mut ValueSerializer) -> Result<bool>>(
429        rt: &Runtime,
430        mut w: F,
431    ) -> Result<bool> {
432        rt.context().with(|c| {
433            let mut serializer = ValueSerializer::from_context(c.clone()).unwrap();
434            w(&mut serializer)
435        })
436    }
437
438    quickcheck! {
439        fn test_i16(v: i16) -> Result<bool> {
440            let rt = Runtime::default();
441            with_serializer(&rt, |serializer| {
442                let value = serializer.serialize_i16(v)?;
443                Ok(value.is_int())
444            })
445        }
446
447        fn test_i32(v: i32) -> Result<bool> {
448            let rt = Runtime::default();
449            with_serializer(&rt, |serializer| {
450                let value = serializer.serialize_i32(v)?;
451                Ok(value.is_int())
452            })
453        }
454
455        fn test_i64(v: i64) -> Result<bool> {
456            let rt = Runtime::default();
457            with_serializer(&rt, |serializer| {
458                if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&v) {
459                    let value = serializer.serialize_i64(v)?;
460                    Ok(value.is_number())
461                } else {
462                    let value = serializer.serialize_f64(v as f64)?;
463                    Ok(value.is_number())
464                }
465            })
466        }
467
468        fn test_u64(v: u64) -> Result<bool> {
469            let rt = Runtime::default();
470            with_serializer(&rt, |serializer| {
471                if v <= MAX_SAFE_INTEGER as u64 {
472                    let value =  serializer.serialize_u64(v)?;
473                    Ok(value.is_number())
474                } else {
475                    let value =  serializer.serialize_f64(v as f64)?;
476                    Ok(value.is_number())
477                }
478            })
479        }
480
481        fn test_u16(v: u16) -> Result<bool> {
482            let rt = Runtime::default();
483
484            with_serializer(&rt, |serializer| {
485                let value = serializer.serialize_u16(v)?;
486                Ok(value.is_int())
487            })
488        }
489
490        fn test_u32(v: u32) -> Result<bool> {
491            let rt = Runtime::default();
492            with_serializer(&rt, |serializer| {
493                let value = serializer.serialize_u32(v)?;
494                // QuickJS optimizes numbers in the range of [i32::MIN..=i32::MAX]
495                // as ints
496                if v > i32::MAX as u32 {
497                    Ok(value.is_float())
498                } else {
499                    Ok(value.is_int())
500                }
501            })
502
503        }
504
505        fn test_f32(v: f32) -> Result<bool> {
506            let rt = Runtime::default();
507            with_serializer(&rt, |serializer| {
508                let value = serializer.serialize_f32(v)?;
509
510                if v == 0.0_f32 {
511                    if v.is_sign_positive() {
512                        return  Ok(value.is_int());
513                    }
514
515
516                    if v.is_sign_negative() {
517                        return Ok(value.is_float());
518                    }
519                }
520
521                // The same (int) optimization is happening at this point,
522                // but here we need to account for signs
523                let zero_fractional_part = v.fract() == 0.0;
524                let range = (i32::MIN as f32)..=(i32::MAX as f32);
525
526                if zero_fractional_part && range.contains(&v) {
527                    Ok(value.is_int())
528                } else {
529                    Ok(value.is_float())
530                }
531            })
532        }
533
534        fn test_f64(v: f64) -> Result<bool> {
535            let rt = Runtime::default();
536            with_serializer(&rt, |serializer| {
537                let value = serializer.serialize_f64(v)?;
538
539                if v == 0.0_f64 {
540                    if v.is_sign_positive() {
541                        return  Ok(value.is_int());
542                    }
543
544
545                    if v.is_sign_negative() {
546                        return Ok(value.is_float());
547                    }
548                }
549
550                // The same (int) optimization is happening at this point,
551                // but here we need to account for signs
552                let zero_fractional_part = v.fract() == 0.0;
553                let range = (i32::MIN as f64)..=(i32::MAX as f64);
554
555                if zero_fractional_part && range.contains(&v) {
556                    Ok(value.is_int())
557                } else {
558                    Ok(value.is_float())
559                }
560            })
561        }
562
563        fn test_bool(v: bool) -> Result<bool> {
564            let rt = Runtime::default();
565            with_serializer(&rt, |serializer| {
566                let value =  serializer.serialize_bool(v)?;
567                Ok(value.is_bool())
568            })
569        }
570
571        fn test_str(v: String) -> Result<bool> {
572            let rt = Runtime::default();
573            with_serializer(&rt, |serializer| {
574                let value = serializer.serialize_str(v.as_str())?;
575
576                Ok(value.is_string())
577            })
578        }
579    }
580
581    #[test]
582    fn test_null() -> Result<()> {
583        let rt = Runtime::default();
584
585        rt.context().with(|cx| {
586            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
587            let value = serializer.serialize_unit().unwrap();
588
589            assert!(value.is_null());
590        });
591        Ok(())
592    }
593
594    #[test]
595    fn test_nan() -> Result<()> {
596        let rt = Runtime::default();
597
598        rt.context().with(|cx| {
599            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
600            let value = serializer.serialize_f64(f64::NAN).unwrap();
601            assert!(value.is_number());
602        });
603        Ok(())
604    }
605
606    #[test]
607    fn test_infinity() -> Result<()> {
608        let rt = Runtime::default();
609        rt.context().with(|cx| {
610            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
611            let value = serializer.serialize_f64(f64::INFINITY).unwrap();
612            assert!(value.is_number());
613        });
614        Ok(())
615    }
616
617    #[test]
618    fn test_negative_infinity() -> Result<()> {
619        let rt = Runtime::default();
620        rt.context().with(|cx| {
621            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
622            let value = serializer.serialize_f64(f64::NEG_INFINITY).unwrap();
623            assert!(value.is_number());
624        });
625        Ok(())
626    }
627
628    #[test]
629    fn test_map() {
630        let rt = Runtime::default();
631
632        rt.context().with(|cx| {
633            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
634
635            let mut map = BTreeMap::new();
636            map.insert("foo", "bar");
637            map.insert("toto", "titi");
638
639            let value = map.serialize(&mut serializer).unwrap();
640
641            assert!(value.is_object())
642        });
643    }
644
645    #[test]
646    fn test_map_proto_key_is_own_data_property() {
647        let rt = Runtime::default();
648
649        rt.context().with(|cx| {
650            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
651
652            let mut map = BTreeMap::new();
653            map.insert("__proto__", "bar");
654
655            let value = map.serialize(&mut serializer).unwrap();
656
657            assert_has_own_proto_data_property(cx, value, "'bar'");
658        });
659    }
660
661    #[test]
662    fn test_struct_into_map() {
663        let rt = Runtime::default();
664
665        rt.context().with(|cx| {
666            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
667
668            #[derive(serde::Serialize)]
669            struct MyObject {
670                foo: String,
671                bar: u32,
672            }
673
674            let my_object = MyObject {
675                foo: "hello".to_string(),
676                bar: 1337,
677            };
678            let value = my_object.serialize(&mut serializer).unwrap();
679
680            assert!(value.is_object());
681        });
682    }
683
684    #[test]
685    fn test_sequence() {
686        let rt = Runtime::default();
687
688        rt.context().with(|cx| {
689            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
690
691            let sequence = vec!["hello", "world"];
692
693            let value = sequence.serialize(&mut serializer).unwrap();
694
695            assert!(value.is_array());
696            assert_eq!(r#"["hello","world"]"#, json_stringify(cx.clone(), value));
697        });
698    }
699
700    #[test]
701    fn test_enum_unit() {
702        let rt = Runtime::default();
703
704        #[derive(Debug, Clone, Copy, PartialEq, Serialize)]
705        enum Test {
706            One,
707        }
708
709        rt.context().with(|cx| {
710            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
711
712            let src = Test::One;
713
714            let value = src.serialize(&mut serializer).unwrap();
715
716            let inner: String = value.as_string().unwrap().to_string().unwrap();
717            assert_eq!("One", inner);
718        });
719    }
720
721    #[test]
722    fn test_enum_newtype() {
723        let rt = Runtime::default();
724
725        #[derive(Debug, Clone, Copy, PartialEq, Serialize)]
726        enum Test {
727            Item(i32),
728        }
729
730        rt.context().with(|cx| {
731            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
732            let src = Test::Item(10);
733            let value = src.serialize(&mut serializer).unwrap();
734
735            assert_eq!(r#"{"Item":10}"#, json_stringify(cx.clone(), value));
736        });
737    }
738
739    #[test]
740    fn test_enum_struct() {
741        let rt = Runtime::default();
742
743        #[derive(Debug, Clone, Copy, PartialEq, Serialize)]
744        enum Test {
745            One { a: i32 },
746        }
747
748        rt.context().with(|cx| {
749            let src = Test::One { a: 6 };
750
751            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
752            let value = src.serialize(&mut serializer).unwrap();
753
754            assert_eq!(r#"{"One":{"a":6}}"#, json_stringify(cx.clone(), value));
755        });
756    }
757
758    #[test]
759    fn test_enum_tuple() {
760        let rt = Runtime::default();
761
762        #[derive(Debug, Clone, Copy, PartialEq, Serialize)]
763        enum Test {
764            One(i32, i32),
765        }
766
767        rt.context().with(|cx| {
768            let mut serializer = ValueSerializer::from_context(cx.clone()).unwrap();
769            let src = Test::One(1, 2);
770            let value = src.serialize(&mut serializer).unwrap();
771
772            assert_eq!(r#"{"One":[1,2]}"#, json_stringify(cx.clone(), value));
773        });
774    }
775
776    fn assert_has_own_proto_data_property<'js>(
777        cx: Ctx<'js>,
778        value: Value<'js>,
779        expected_value_source: &str,
780    ) {
781        cx.globals().set("__rquickjs_serde_value", value).unwrap();
782        let assertion = format!(
783            "Object.getPrototypeOf(__rquickjs_serde_value) === Object.prototype && \
784             Object.prototype.hasOwnProperty.call(__rquickjs_serde_value, '__proto__') && \
785             Object.getOwnPropertyDescriptor(__rquickjs_serde_value, '__proto__').value === {expected_value_source}"
786        );
787        assert!(cx.eval::<bool, _>(assertion.as_str()).unwrap());
788    }
789
790    fn json_stringify<'js>(cx: Ctx<'js>, value: Value<'js>) -> String {
791        let obj: Object = cx.globals().get("JSON").unwrap();
792        let stringify: Function = obj.get("stringify").unwrap();
793        let str: String = stringify.call((value,)).unwrap();
794        str
795    }
796}