mozjs/
conversions.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! Conversions of Rust values to and from `JSVal`.
6//!
7//! | IDL type                | Type                             |
8//! |-------------------------|----------------------------------|
9//! | any                     | `JSVal`                          |
10//! | boolean                 | `bool`                           |
11//! | byte                    | `i8`                             |
12//! | octet                   | `u8`                             |
13//! | short                   | `i16`                            |
14//! | unsigned short          | `u16`                            |
15//! | long                    | `i32`                            |
16//! | unsigned long           | `u32`                            |
17//! | long long               | `i64`                            |
18//! | unsigned long long      | `u64`                            |
19//! | unrestricted float      | `f32`                            |
20//! | float                   | `Finite<f32>`                    |
21//! | unrestricted double     | `f64`                            |
22//! | double                  | `Finite<f64>`                    |
23//! | USVString               | `String`                         |
24//! | object                  | `*mut JSObject`                  |
25//! | symbol                  | `*mut Symbol`                    |
26//! | nullable types          | `Option<T>`                      |
27//! | sequences               | `Vec<T>`                         |
28
29#![deny(missing_docs)]
30
31use crate::error::throw_type_error;
32use crate::jsapi::AssertSameCompartment;
33use crate::jsapi::JS;
34use crate::jsapi::{ForOfIterator, ForOfIterator_NonIterableBehavior};
35use crate::jsapi::{Heap, JS_DefineElement, JS_GetLatin1StringCharsAndLength};
36use crate::jsapi::{JSContext, JSObject, JSString, RootedObject, RootedValue};
37use crate::jsapi::{JS_DeprecatedStringHasLatin1Chars, JS_NewStringCopyUTF8N, JSPROP_ENUMERATE};
38use crate::jsapi::{JS_GetTwoByteStringCharsAndLength, NewArrayObject1};
39use crate::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
40use crate::jsval::{JSVal, ObjectOrNullValue, ObjectValue, StringValue, SymbolValue};
41use crate::rooted;
42use crate::rust::maybe_wrap_value;
43use crate::rust::{maybe_wrap_object_or_null_value, maybe_wrap_object_value, ToString};
44use crate::rust::{HandleValue, MutableHandleValue};
45use crate::rust::{ToBoolean, ToInt32, ToInt64, ToNumber, ToUint16, ToUint32, ToUint64};
46use libc;
47use log::debug;
48use mozjs_sys::jsgc::Rooted;
49use std::borrow::Cow;
50use std::mem;
51use std::ptr::NonNull;
52use std::rc::Rc;
53use std::{ptr, slice};
54
55trait As<O>: Copy {
56    fn cast(self) -> O;
57}
58
59macro_rules! impl_as {
60    ($I:ty, $O:ty) => {
61        impl As<$O> for $I {
62            fn cast(self) -> $O {
63                self as $O
64            }
65        }
66    };
67}
68
69impl_as!(f64, u8);
70impl_as!(f64, u16);
71impl_as!(f64, u32);
72impl_as!(f64, u64);
73impl_as!(f64, i8);
74impl_as!(f64, i16);
75impl_as!(f64, i32);
76impl_as!(f64, i64);
77
78impl_as!(u8, f64);
79impl_as!(u16, f64);
80impl_as!(u32, f64);
81impl_as!(u64, f64);
82impl_as!(i8, f64);
83impl_as!(i16, f64);
84impl_as!(i32, f64);
85impl_as!(i64, f64);
86
87impl_as!(i32, i8);
88impl_as!(i32, u8);
89impl_as!(i32, i16);
90impl_as!(u16, u16);
91impl_as!(i32, i32);
92impl_as!(u32, u32);
93impl_as!(i64, i64);
94impl_as!(u64, u64);
95
96/// Similar to num_traits, but we use need to be able to customize values
97pub trait Number {
98    /// Zero value of this type
99    const ZERO: Self;
100    /// Smallest finite number this type can represent
101    const MIN: Self;
102    /// Largest finite number this type can represent
103    const MAX: Self;
104}
105
106macro_rules! impl_num {
107    ($N:ty, $zero:expr, $min:expr, $max:expr) => {
108        impl Number for $N {
109            const ZERO: $N = $zero;
110            const MIN: $N = $min;
111            const MAX: $N = $max;
112        }
113    };
114}
115
116// lower upper bound per: https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
117impl_num!(u8, 0, u8::MIN, u8::MAX);
118impl_num!(u16, 0, u16::MIN, u16::MAX);
119impl_num!(u32, 0, u32::MIN, u32::MAX);
120impl_num!(u64, 0, 0, (1 << 53) - 1);
121
122impl_num!(i8, 0, i8::MIN, i8::MAX);
123impl_num!(i16, 0, i16::MIN, i16::MAX);
124impl_num!(i32, 0, i32::MIN, i32::MAX);
125impl_num!(i64, 0, -(1 << 53) + 1, (1 << 53) - 1);
126
127impl_num!(f32, 0.0, f32::MIN, f32::MAX);
128impl_num!(f64, 0.0, f64::MIN, f64::MAX);
129
130/// A trait to convert Rust types to `JSVal`s.
131pub trait ToJSValConvertible {
132    /// Convert `self` to a `JSVal`. JSAPI failure causes a panic.
133    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue);
134}
135
136/// An enum to better support enums through FromJSValConvertible::from_jsval.
137#[derive(PartialEq, Eq, Clone, Debug)]
138pub enum ConversionResult<T> {
139    /// Everything went fine.
140    Success(T),
141    /// Conversion failed, without a pending exception.
142    Failure(Cow<'static, str>),
143}
144
145impl<T> ConversionResult<T> {
146    /// Returns Some(value) if it is `ConversionResult::Success`.
147    pub fn get_success_value(&self) -> Option<&T> {
148        match *self {
149            ConversionResult::Success(ref v) => Some(v),
150            _ => None,
151        }
152    }
153}
154
155/// A trait to convert `JSVal`s to Rust types.
156pub trait FromJSValConvertible: Sized {
157    /// Optional configurable behaviour switch; use () for no configuration.
158    type Config;
159    /// Convert `val` to type `Self`.
160    /// Optional configuration of type `T` can be passed as the `option`
161    /// argument.
162    /// If it returns `Err(())`, a JSAPI exception is pending.
163    /// If it returns `Ok(Failure(reason))`, there is no pending JSAPI exception.
164    unsafe fn from_jsval(
165        cx: *mut JSContext,
166        val: HandleValue,
167        option: Self::Config,
168    ) -> Result<ConversionResult<Self>, ()>;
169}
170
171/// A trait to convert `JSVal`s to Rust types inside of Rc wrappers.
172pub trait FromJSValConvertibleRc: Sized {
173    /// Convert `val` to type `Self`.
174    /// If it returns `Err(())`, a JSAPI exception is pending.
175    /// If it returns `Ok(Failure(reason))`, there is no pending JSAPI exception.
176    unsafe fn from_jsval(
177        cx: *mut JSContext,
178        val: HandleValue,
179    ) -> Result<ConversionResult<Rc<Self>>, ()>;
180}
181
182impl<T: FromJSValConvertibleRc> FromJSValConvertible for Rc<T> {
183    type Config = ();
184
185    unsafe fn from_jsval(
186        cx: *mut JSContext,
187        val: HandleValue,
188        _option: (),
189    ) -> Result<ConversionResult<Rc<T>>, ()> {
190        <T as FromJSValConvertibleRc>::from_jsval(cx, val)
191    }
192}
193
194/// Behavior for converting out-of-range integers.
195#[derive(PartialEq, Eq, Clone)]
196pub enum ConversionBehavior {
197    /// Wrap into the integer's range.
198    Default,
199    /// Throw an exception.
200    EnforceRange,
201    /// Clamp into the integer's range.
202    Clamp,
203}
204
205/// Try to cast the number to a smaller type, but
206/// if it doesn't fit, it will return an error.
207// https://searchfox.org/mozilla-esr128/rev/1aa97f9d67f7a7231e62af283eaa02a6b31380e1/dom/bindings/PrimitiveConversions.h#166
208unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
209where
210    D: Number + As<f64>,
211    f64: As<D>,
212{
213    if d.is_infinite() {
214        throw_type_error(cx, "value out of range in an EnforceRange argument");
215        return Err(());
216    }
217
218    let rounded = d.signum() * d.abs().floor();
219    if D::MIN.cast() <= rounded && rounded <= D::MAX.cast() {
220        Ok(ConversionResult::Success(rounded.cast()))
221    } else {
222        throw_type_error(cx, "value out of range in an EnforceRange argument");
223        Err(())
224    }
225}
226
227/// Try to cast the number to a smaller type, but if it doesn't fit,
228/// round it to the MAX or MIN of the source type before casting it to
229/// the destination type.
230fn clamp_to<D>(d: f64) -> D
231where
232    D: Number + As<f64>,
233    f64: As<D>,
234{
235    if d.is_nan() {
236        D::ZERO
237    } else if d > D::MAX.cast() {
238        D::MAX
239    } else if d < D::MIN.cast() {
240        D::MIN
241    } else {
242        d.cast()
243    }
244}
245
246// https://heycam.github.io/webidl/#es-void
247impl ToJSValConvertible for () {
248    #[inline]
249    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
250        rval.set(UndefinedValue());
251    }
252}
253
254impl FromJSValConvertible for JSVal {
255    type Config = ();
256    unsafe fn from_jsval(
257        _cx: *mut JSContext,
258        value: HandleValue,
259        _option: (),
260    ) -> Result<ConversionResult<JSVal>, ()> {
261        Ok(ConversionResult::Success(value.get()))
262    }
263}
264
265impl ToJSValConvertible for JSVal {
266    #[inline]
267    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
268        rval.set(*self);
269        maybe_wrap_value(cx, rval);
270    }
271}
272
273impl<'a> ToJSValConvertible for HandleValue<'a> {
274    #[inline]
275    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
276        rval.set(self.get());
277        maybe_wrap_value(cx, rval);
278    }
279}
280
281impl ToJSValConvertible for Heap<JSVal> {
282    #[inline]
283    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
284        rval.set(self.get());
285        maybe_wrap_value(cx, rval);
286    }
287}
288
289#[inline]
290unsafe fn convert_int_from_jsval<T, M>(
291    cx: *mut JSContext,
292    value: HandleValue,
293    option: ConversionBehavior,
294    convert_fn: unsafe fn(*mut JSContext, HandleValue) -> Result<M, ()>,
295) -> Result<ConversionResult<T>, ()>
296where
297    T: Number + As<f64>,
298    M: Number + As<T>,
299    f64: As<T>,
300{
301    match option {
302        ConversionBehavior::Default => Ok(ConversionResult::Success(convert_fn(cx, value)?.cast())),
303        ConversionBehavior::EnforceRange => enforce_range(cx, ToNumber(cx, value)?),
304        ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(ToNumber(cx, value)?))),
305    }
306}
307
308// https://heycam.github.io/webidl/#es-boolean
309impl ToJSValConvertible for bool {
310    #[inline]
311    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
312        rval.set(BooleanValue(*self));
313    }
314}
315
316// https://heycam.github.io/webidl/#es-boolean
317impl FromJSValConvertible for bool {
318    type Config = ();
319    unsafe fn from_jsval(
320        _cx: *mut JSContext,
321        val: HandleValue,
322        _option: (),
323    ) -> Result<ConversionResult<bool>, ()> {
324        Ok(ToBoolean(val)).map(ConversionResult::Success)
325    }
326}
327
328// https://heycam.github.io/webidl/#es-byte
329impl ToJSValConvertible for i8 {
330    #[inline]
331    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
332        rval.set(Int32Value(*self as i32));
333    }
334}
335
336// https://heycam.github.io/webidl/#es-byte
337impl FromJSValConvertible for i8 {
338    type Config = ConversionBehavior;
339    unsafe fn from_jsval(
340        cx: *mut JSContext,
341        val: HandleValue,
342        option: ConversionBehavior,
343    ) -> Result<ConversionResult<i8>, ()> {
344        convert_int_from_jsval(cx, val, option, ToInt32)
345    }
346}
347
348// https://heycam.github.io/webidl/#es-octet
349impl ToJSValConvertible for u8 {
350    #[inline]
351    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
352        rval.set(Int32Value(*self as i32));
353    }
354}
355
356// https://heycam.github.io/webidl/#es-octet
357impl FromJSValConvertible for u8 {
358    type Config = ConversionBehavior;
359    unsafe fn from_jsval(
360        cx: *mut JSContext,
361        val: HandleValue,
362        option: ConversionBehavior,
363    ) -> Result<ConversionResult<u8>, ()> {
364        convert_int_from_jsval(cx, val, option, ToInt32)
365    }
366}
367
368// https://heycam.github.io/webidl/#es-short
369impl ToJSValConvertible for i16 {
370    #[inline]
371    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
372        rval.set(Int32Value(*self as i32));
373    }
374}
375
376// https://heycam.github.io/webidl/#es-short
377impl FromJSValConvertible for i16 {
378    type Config = ConversionBehavior;
379    unsafe fn from_jsval(
380        cx: *mut JSContext,
381        val: HandleValue,
382        option: ConversionBehavior,
383    ) -> Result<ConversionResult<i16>, ()> {
384        convert_int_from_jsval(cx, val, option, ToInt32)
385    }
386}
387
388// https://heycam.github.io/webidl/#es-unsigned-short
389impl ToJSValConvertible for u16 {
390    #[inline]
391    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
392        rval.set(Int32Value(*self as i32));
393    }
394}
395
396// https://heycam.github.io/webidl/#es-unsigned-short
397impl FromJSValConvertible for u16 {
398    type Config = ConversionBehavior;
399    unsafe fn from_jsval(
400        cx: *mut JSContext,
401        val: HandleValue,
402        option: ConversionBehavior,
403    ) -> Result<ConversionResult<u16>, ()> {
404        convert_int_from_jsval(cx, val, option, ToUint16)
405    }
406}
407
408// https://heycam.github.io/webidl/#es-long
409impl ToJSValConvertible for i32 {
410    #[inline]
411    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
412        rval.set(Int32Value(*self));
413    }
414}
415
416// https://heycam.github.io/webidl/#es-long
417impl FromJSValConvertible for i32 {
418    type Config = ConversionBehavior;
419    unsafe fn from_jsval(
420        cx: *mut JSContext,
421        val: HandleValue,
422        option: ConversionBehavior,
423    ) -> Result<ConversionResult<i32>, ()> {
424        convert_int_from_jsval(cx, val, option, ToInt32)
425    }
426}
427
428// https://heycam.github.io/webidl/#es-unsigned-long
429impl ToJSValConvertible for u32 {
430    #[inline]
431    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
432        rval.set(UInt32Value(*self));
433    }
434}
435
436// https://heycam.github.io/webidl/#es-unsigned-long
437impl FromJSValConvertible for u32 {
438    type Config = ConversionBehavior;
439    unsafe fn from_jsval(
440        cx: *mut JSContext,
441        val: HandleValue,
442        option: ConversionBehavior,
443    ) -> Result<ConversionResult<u32>, ()> {
444        convert_int_from_jsval(cx, val, option, ToUint32)
445    }
446}
447
448// https://heycam.github.io/webidl/#es-long-long
449impl ToJSValConvertible for i64 {
450    #[inline]
451    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
452        rval.set(DoubleValue(*self as f64));
453    }
454}
455
456// https://heycam.github.io/webidl/#es-long-long
457impl FromJSValConvertible for i64 {
458    type Config = ConversionBehavior;
459    unsafe fn from_jsval(
460        cx: *mut JSContext,
461        val: HandleValue,
462        option: ConversionBehavior,
463    ) -> Result<ConversionResult<i64>, ()> {
464        convert_int_from_jsval(cx, val, option, ToInt64)
465    }
466}
467
468// https://heycam.github.io/webidl/#es-unsigned-long-long
469impl ToJSValConvertible for u64 {
470    #[inline]
471    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
472        rval.set(DoubleValue(*self as f64));
473    }
474}
475
476// https://heycam.github.io/webidl/#es-unsigned-long-long
477impl FromJSValConvertible for u64 {
478    type Config = ConversionBehavior;
479    unsafe fn from_jsval(
480        cx: *mut JSContext,
481        val: HandleValue,
482        option: ConversionBehavior,
483    ) -> Result<ConversionResult<u64>, ()> {
484        convert_int_from_jsval(cx, val, option, ToUint64)
485    }
486}
487
488// https://heycam.github.io/webidl/#es-float
489impl ToJSValConvertible for f32 {
490    #[inline]
491    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
492        rval.set(DoubleValue(*self as f64));
493    }
494}
495
496// https://heycam.github.io/webidl/#es-float
497impl FromJSValConvertible for f32 {
498    type Config = ();
499    unsafe fn from_jsval(
500        cx: *mut JSContext,
501        val: HandleValue,
502        _option: (),
503    ) -> Result<ConversionResult<f32>, ()> {
504        let result = ToNumber(cx, val);
505        result.map(|f| f as f32).map(ConversionResult::Success)
506    }
507}
508
509// https://heycam.github.io/webidl/#es-double
510impl ToJSValConvertible for f64 {
511    #[inline]
512    unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
513        rval.set(DoubleValue(*self))
514    }
515}
516
517// https://heycam.github.io/webidl/#es-double
518impl FromJSValConvertible for f64 {
519    type Config = ();
520    unsafe fn from_jsval(
521        cx: *mut JSContext,
522        val: HandleValue,
523        _option: (),
524    ) -> Result<ConversionResult<f64>, ()> {
525        ToNumber(cx, val).map(ConversionResult::Success)
526    }
527}
528
529/// Converts a `JSString`, encoded in "Latin1" (i.e. U+0000-U+00FF encoded as 0x00-0xFF) into a
530/// `String`.
531pub unsafe fn latin1_to_string(cx: *mut JSContext, s: NonNull<JSString>) -> String {
532    assert!(JS_DeprecatedStringHasLatin1Chars(s.as_ptr()));
533
534    let mut length = 0;
535    let chars = unsafe {
536        let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s.as_ptr(), &mut length);
537        assert!(!chars.is_null());
538
539        slice::from_raw_parts(chars, length as usize)
540    };
541    // The `encoding.rs` documentation for `convert_latin1_to_utf8` states that:
542    // > The length of the destination buffer must be at least the length of the source
543    // > buffer times two.
544    let mut v = vec![0; chars.len() * 2];
545    let real_size = encoding_rs::mem::convert_latin1_to_utf8(chars, v.as_mut_slice());
546
547    v.truncate(real_size);
548
549    // Safety: convert_latin1_to_utf8 converts the raw bytes to utf8 and the
550    // buffer is the size specified in the documentation, so this should be safe.
551    unsafe { String::from_utf8_unchecked(v) }
552}
553
554/// Converts a `JSString` into a `String`, regardless of used encoding.
555pub unsafe fn jsstr_to_string(cx: *mut JSContext, jsstr: NonNull<JSString>) -> String {
556    if JS_DeprecatedStringHasLatin1Chars(jsstr.as_ptr()) {
557        return latin1_to_string(cx, jsstr);
558    }
559
560    let mut length = 0;
561    let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr.as_ptr(), &mut length);
562    assert!(!chars.is_null());
563    let char_vec = slice::from_raw_parts(chars, length as usize);
564    String::from_utf16_lossy(char_vec)
565}
566
567// https://heycam.github.io/webidl/#es-USVString
568impl ToJSValConvertible for str {
569    #[inline]
570    #[deny(unsafe_op_in_unsafe_fn)]
571    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
572        // Spidermonkey will automatically only copy latin1
573        // or similar if the given encoding can be small enough.
574        // So there is no need to distinguish between ascii only or similar.
575        let s = Utf8Chars::from(self);
576        let jsstr = unsafe { JS_NewStringCopyUTF8N(cx, &*s as *const _) };
577        if jsstr.is_null() {
578            panic!("JS String copy routine failed");
579        }
580        unsafe {
581            rval.set(StringValue(&*jsstr));
582        }
583    }
584}
585
586// https://heycam.github.io/webidl/#es-USVString
587impl ToJSValConvertible for String {
588    #[inline]
589    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
590        (**self).to_jsval(cx, rval);
591    }
592}
593
594// https://heycam.github.io/webidl/#es-USVString
595impl FromJSValConvertible for String {
596    type Config = ();
597    unsafe fn from_jsval(
598        cx: *mut JSContext,
599        value: HandleValue,
600        _: (),
601    ) -> Result<ConversionResult<String>, ()> {
602        let jsstr = ToString(cx, value);
603        let Some(jsstr) = NonNull::new(jsstr) else {
604            debug!("ToString failed");
605            return Err(());
606        };
607        Ok(jsstr_to_string(cx, jsstr)).map(ConversionResult::Success)
608    }
609}
610
611impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
612    #[inline]
613    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
614        match self {
615            &Some(ref value) => value.to_jsval(cx, rval),
616            &None => rval.set(NullValue()),
617        }
618    }
619}
620
621impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
622    type Config = T::Config;
623    unsafe fn from_jsval(
624        cx: *mut JSContext,
625        value: HandleValue,
626        option: T::Config,
627    ) -> Result<ConversionResult<Option<T>>, ()> {
628        if value.get().is_null_or_undefined() {
629            Ok(ConversionResult::Success(None))
630        } else {
631            Ok(match FromJSValConvertible::from_jsval(cx, value, option)? {
632                ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
633                ConversionResult::Failure(v) => ConversionResult::Failure(v),
634            })
635        }
636    }
637}
638
639impl<T: ToJSValConvertible> ToJSValConvertible for &'_ T {
640    #[inline]
641    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
642        (**self).to_jsval(cx, rval)
643    }
644}
645
646impl<T: ToJSValConvertible> ToJSValConvertible for Box<T> {
647    #[inline]
648    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
649        (**self).to_jsval(cx, rval)
650    }
651}
652
653impl<T: ToJSValConvertible> ToJSValConvertible for Rc<T> {
654    #[inline]
655    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
656        (**self).to_jsval(cx, rval)
657    }
658}
659
660// https://heycam.github.io/webidl/#es-sequence
661impl<T: ToJSValConvertible> ToJSValConvertible for [T] {
662    #[inline]
663    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
664        rooted!(in(cx) let js_array = NewArrayObject1(cx, self.len() as libc::size_t));
665        assert!(!js_array.handle().is_null());
666
667        rooted!(in(cx) let mut val = UndefinedValue());
668        for (index, obj) in self.iter().enumerate() {
669            obj.to_jsval(cx, val.handle_mut());
670
671            assert!(JS_DefineElement(
672                cx,
673                js_array.handle().into(),
674                index as u32,
675                val.handle().into(),
676                JSPROP_ENUMERATE as u32
677            ));
678        }
679
680        rval.set(ObjectValue(js_array.handle().get()));
681    }
682}
683
684// https://heycam.github.io/webidl/#es-sequence
685impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
686    #[inline]
687    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
688        <[_]>::to_jsval(self, cx, rval)
689    }
690}
691
692/// Rooting guard for the iterator field of ForOfIterator.
693/// Behaves like RootedGuard (roots on creation, unroots on drop),
694/// but borrows and allows access to the whole ForOfIterator, so
695/// that methods on ForOfIterator can still be used through it.
696struct ForOfIteratorGuard<'a> {
697    root: &'a mut ForOfIterator,
698}
699
700impl<'a> ForOfIteratorGuard<'a> {
701    fn new(cx: *mut JSContext, root: &'a mut ForOfIterator) -> Self {
702        unsafe {
703            Rooted::add_to_root_stack(&raw mut root.iterator, cx);
704        }
705        ForOfIteratorGuard { root }
706    }
707}
708
709impl<'a> Drop for ForOfIteratorGuard<'a> {
710    fn drop(&mut self) {
711        unsafe {
712            self.root.iterator.remove_from_root_stack();
713        }
714    }
715}
716
717impl<C: Clone, T: FromJSValConvertible<Config = C>> FromJSValConvertible for Vec<T> {
718    type Config = C;
719
720    unsafe fn from_jsval(
721        cx: *mut JSContext,
722        value: HandleValue,
723        option: C,
724    ) -> Result<ConversionResult<Vec<T>>, ()> {
725        if !value.is_object() {
726            return Ok(ConversionResult::Failure("Value is not an object".into()));
727        }
728
729        // Depending on the version of LLVM in use, bindgen can end up including
730        // a padding field in the ForOfIterator. To support multiple versions of
731        // LLVM that may not have the same fields as a result, we create an empty
732        // iterator instance and initialize a non-empty instance using the empty
733        // instance as a base value.
734        let zero = mem::zeroed();
735        let mut iterator = ForOfIterator {
736            cx_: cx,
737            iterator: RootedObject::new_unrooted(ptr::null_mut()),
738            nextMethod: RootedValue::new_unrooted(JSVal { asBits_: 0 }),
739            index: ::std::u32::MAX, // NOT_ARRAY
740            ..zero
741        };
742        let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
743        let iterator: &mut ForOfIterator = &mut *iterator.root;
744
745        if !iterator.init(
746            value.into(),
747            ForOfIterator_NonIterableBehavior::AllowNonIterable,
748        ) {
749            return Err(());
750        }
751
752        if iterator.iterator.data.is_null() {
753            return Ok(ConversionResult::Failure("Value is not iterable".into()));
754        }
755
756        let mut ret = vec![];
757
758        loop {
759            let mut done = false;
760            rooted!(in(cx) let mut val = UndefinedValue());
761            if !iterator.next(val.handle_mut().into(), &mut done) {
762                return Err(());
763            }
764
765            if done {
766                break;
767            }
768
769            ret.push(match T::from_jsval(cx, val.handle(), option.clone())? {
770                ConversionResult::Success(v) => v,
771                ConversionResult::Failure(e) => {
772                    throw_type_error(cx, &e);
773                    return Err(());
774                }
775            });
776        }
777
778        Ok(ret).map(ConversionResult::Success)
779    }
780}
781
782// https://heycam.github.io/webidl/#es-object
783impl ToJSValConvertible for *mut JSObject {
784    #[inline]
785    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
786        rval.set(ObjectOrNullValue(*self));
787        maybe_wrap_object_or_null_value(cx, rval);
788    }
789}
790
791// https://heycam.github.io/webidl/#es-object
792impl ToJSValConvertible for ptr::NonNull<JSObject> {
793    #[inline]
794    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
795        rval.set(ObjectValue(self.as_ptr()));
796        maybe_wrap_object_value(cx, rval);
797    }
798}
799
800// https://heycam.github.io/webidl/#es-object
801impl ToJSValConvertible for Heap<*mut JSObject> {
802    #[inline]
803    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
804        rval.set(ObjectOrNullValue(self.get()));
805        maybe_wrap_object_or_null_value(cx, rval);
806    }
807}
808
809// https://heycam.github.io/webidl/#es-object
810impl FromJSValConvertible for *mut JSObject {
811    type Config = ();
812    #[inline]
813    unsafe fn from_jsval(
814        cx: *mut JSContext,
815        value: HandleValue,
816        _option: (),
817    ) -> Result<ConversionResult<*mut JSObject>, ()> {
818        if !value.is_object() {
819            throw_type_error(cx, "value is not an object");
820            return Err(());
821        }
822
823        AssertSameCompartment(cx, value.to_object());
824
825        Ok(ConversionResult::Success(value.to_object()))
826    }
827}
828
829impl ToJSValConvertible for *mut JS::Symbol {
830    #[inline]
831    unsafe fn to_jsval(&self, _: *mut JSContext, mut rval: MutableHandleValue) {
832        rval.set(SymbolValue(&**self));
833    }
834}
835
836impl FromJSValConvertible for *mut JS::Symbol {
837    type Config = ();
838    #[inline]
839    unsafe fn from_jsval(
840        cx: *mut JSContext,
841        value: HandleValue,
842        _option: (),
843    ) -> Result<ConversionResult<*mut JS::Symbol>, ()> {
844        if !value.is_symbol() {
845            throw_type_error(cx, "value is not a symbol");
846            return Err(());
847        }
848
849        Ok(ConversionResult::Success(value.to_symbol()))
850    }
851}
852
853/// A wrapper type over [`crate::jsapi::UTF8Chars`]. This is created to help transferring
854/// a rust string to mozjs. The inner [`crate::jsapi::UTF8Chars`] can be accessed via the
855/// [`std::ops::Deref`] trait.
856pub struct Utf8Chars<'a> {
857    lt_marker: std::marker::PhantomData<&'a ()>,
858    inner: crate::jsapi::UTF8Chars,
859}
860
861impl<'a> std::ops::Deref for Utf8Chars<'a> {
862    type Target = crate::jsapi::UTF8Chars;
863
864    fn deref(&self) -> &Self::Target {
865        &self.inner
866    }
867}
868
869impl<'a> From<&'a str> for Utf8Chars<'a> {
870    #[allow(unsafe_code)]
871    fn from(value: &'a str) -> Self {
872        use std::marker::PhantomData;
873
874        use crate::jsapi::mozilla::{Range, RangedPtr};
875        use crate::jsapi::UTF8Chars;
876
877        let range = value.as_bytes().as_ptr_range();
878        let range_start = range.start as *mut _;
879        let range_end = range.end as *mut _;
880        let start = RangedPtr {
881            _phantom_0: PhantomData,
882            mPtr: range_start,
883            #[cfg(feature = "debugmozjs")]
884            mRangeStart: range_start,
885            #[cfg(feature = "debugmozjs")]
886            mRangeEnd: range_end,
887        };
888        let end = RangedPtr {
889            _phantom_0: PhantomData,
890            mPtr: range_end,
891            #[cfg(feature = "debugmozjs")]
892            mRangeStart: range_start,
893            #[cfg(feature = "debugmozjs")]
894            mRangeEnd: range_end,
895        };
896        let base = Range {
897            _phantom_0: PhantomData,
898            mStart: start,
899            mEnd: end,
900        };
901        let inner = UTF8Chars { _base: base };
902        Self {
903            lt_marker: PhantomData,
904            inner,
905        }
906    }
907}