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