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