Coverage Report

Created: 2025-06-16 06:50

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