/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 | | } |