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