Skip to main content

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