Skip to content
This repository was archived by the owner on Nov 12, 2022. It is now read-only.

Commit daffcfb

Browse files
author
bors-servo
authored
Auto merge of #284 - GuillaumeGomez:rewrite_from_js_val, r=Ms2ger
Rewrite FromJSValConvertible to better support unions Fixes #282. <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-mozjs/284) <!-- Reviewable:end -->
2 parents 288e587 + 9b2e4ad commit daffcfb

File tree

2 files changed

+60
-35
lines changed

2 files changed

+60
-35
lines changed

src/conversions.rs

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use jsval::{JSVal, ObjectValue, ObjectOrNullValue, StringValue};
4040
use rust::{ToBoolean, ToNumber, ToUint16, ToInt32, ToUint32, ToInt64, ToUint64, ToString};
4141
use libc;
4242
use num_traits::{Bounded, Zero};
43+
use std::borrow::Cow;
4344
use std::rc::Rc;
4445
use std::{ptr, slice};
4546

@@ -91,6 +92,25 @@ pub trait ToJSValConvertible {
9192
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue);
9293
}
9394

95+
/// An enum to better support enums through FromJSValConvertible::from_jsval.
96+
#[derive(PartialEq, Eq, Clone, Debug)]
97+
pub enum ConversionResult<T> {
98+
/// Everything went fine.
99+
Success(T),
100+
/// Pending exception.
101+
Failure(Cow<'static, str>),
102+
}
103+
104+
impl<T> ConversionResult<T> {
105+
/// Returns Some(value) if it is `ConversionResult::Success`.
106+
pub fn get_success_value(&self) -> Option<&T> {
107+
match *self {
108+
ConversionResult::Success(ref v) => Some(v),
109+
_ => None,
110+
}
111+
}
112+
}
113+
94114
/// A trait to convert `JSVal`s to Rust types.
95115
pub trait FromJSValConvertible: Sized {
96116
/// Optional configurable behaviour switch; use () for no configuration.
@@ -102,7 +122,7 @@ pub trait FromJSValConvertible: Sized {
102122
unsafe fn from_jsval(cx: *mut JSContext,
103123
val: HandleValue,
104124
option: Self::Config)
105-
-> Result<Self, ()>;
125+
-> Result<ConversionResult<Self>, ()>;
106126
}
107127

108128
/// Behavior for converting out-of-range integers.
@@ -118,7 +138,7 @@ pub enum ConversionBehavior {
118138

119139
/// Try to cast the number to a smaller type, but
120140
/// if it doesn't fit, it will return an error.
121-
unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<D, ()>
141+
unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
122142
where D: Bounded + As<f64>,
123143
f64: As<D>
124144
{
@@ -129,7 +149,7 @@ unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<D, ()>
129149

130150
let rounded = d.round();
131151
if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() {
132-
Ok(rounded.cast())
152+
Ok(ConversionResult::Success(rounded.cast()))
133153
} else {
134154
throw_type_error(cx, "value out of range in an EnforceRange argument");
135155
Err(())
@@ -177,8 +197,8 @@ impl FromJSValConvertible for JSVal {
177197
unsafe fn from_jsval(_cx: *mut JSContext,
178198
value: HandleValue,
179199
_option: ())
180-
-> Result<JSVal, ()> {
181-
Ok(value.get())
200+
-> Result<ConversionResult<JSVal>, ()> {
201+
Ok(ConversionResult::Success(value.get()))
182202
}
183203
}
184204

@@ -196,15 +216,15 @@ impl ToJSValConvertible for HandleValue {
196216
unsafe fn convert_int_from_jsval<T, M>(cx: *mut JSContext, value: HandleValue,
197217
option: ConversionBehavior,
198218
convert_fn: unsafe fn(*mut JSContext, HandleValue) -> Result<M, ()>)
199-
-> Result<T, ()>
219+
-> Result<ConversionResult<T>, ()>
200220
where T: Bounded + Zero + As<f64>,
201221
M: Zero + As<T>,
202222
f64: As<T>
203223
{
204224
match option {
205-
ConversionBehavior::Default => Ok(try!(convert_fn(cx, value)).cast()),
225+
ConversionBehavior::Default => Ok(ConversionResult::Success(try!(convert_fn(cx, value)).cast())),
206226
ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))),
207-
ConversionBehavior::Clamp => Ok(clamp_to(try!(ToNumber(cx, value)))),
227+
ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(try!(ToNumber(cx, value))))),
208228
}
209229
}
210230

@@ -219,8 +239,8 @@ impl ToJSValConvertible for bool {
219239
// https://heycam.github.io/webidl/#es-boolean
220240
impl FromJSValConvertible for bool {
221241
type Config = ();
222-
unsafe fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<bool, ()> {
223-
Ok(ToBoolean(val))
242+
unsafe fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<ConversionResult<bool>, ()> {
243+
Ok(ToBoolean(val)).map(ConversionResult::Success)
224244
}
225245
}
226246

@@ -238,7 +258,7 @@ impl FromJSValConvertible for i8 {
238258
unsafe fn from_jsval(cx: *mut JSContext,
239259
val: HandleValue,
240260
option: ConversionBehavior)
241-
-> Result<i8, ()> {
261+
-> Result<ConversionResult<i8>, ()> {
242262
convert_int_from_jsval(cx, val, option, ToInt32)
243263
}
244264
}
@@ -257,7 +277,7 @@ impl FromJSValConvertible for u8 {
257277
unsafe fn from_jsval(cx: *mut JSContext,
258278
val: HandleValue,
259279
option: ConversionBehavior)
260-
-> Result<u8, ()> {
280+
-> Result<ConversionResult<u8>, ()> {
261281
convert_int_from_jsval(cx, val, option, ToInt32)
262282
}
263283
}
@@ -276,7 +296,7 @@ impl FromJSValConvertible for i16 {
276296
unsafe fn from_jsval(cx: *mut JSContext,
277297
val: HandleValue,
278298
option: ConversionBehavior)
279-
-> Result<i16, ()> {
299+
-> Result<ConversionResult<i16>, ()> {
280300
convert_int_from_jsval(cx, val, option, ToInt32)
281301
}
282302
}
@@ -295,7 +315,7 @@ impl FromJSValConvertible for u16 {
295315
unsafe fn from_jsval(cx: *mut JSContext,
296316
val: HandleValue,
297317
option: ConversionBehavior)
298-
-> Result<u16, ()> {
318+
-> Result<ConversionResult<u16>, ()> {
299319
convert_int_from_jsval(cx, val, option, ToUint16)
300320
}
301321
}
@@ -314,7 +334,7 @@ impl FromJSValConvertible for i32 {
314334
unsafe fn from_jsval(cx: *mut JSContext,
315335
val: HandleValue,
316336
option: ConversionBehavior)
317-
-> Result<i32, ()> {
337+
-> Result<ConversionResult<i32>, ()> {
318338
convert_int_from_jsval(cx, val, option, ToInt32)
319339
}
320340
}
@@ -333,7 +353,7 @@ impl FromJSValConvertible for u32 {
333353
unsafe fn from_jsval(cx: *mut JSContext,
334354
val: HandleValue,
335355
option: ConversionBehavior)
336-
-> Result<u32, ()> {
356+
-> Result<ConversionResult<u32>, ()> {
337357
convert_int_from_jsval(cx, val, option, ToUint32)
338358
}
339359
}
@@ -352,7 +372,7 @@ impl FromJSValConvertible for i64 {
352372
unsafe fn from_jsval(cx: *mut JSContext,
353373
val: HandleValue,
354374
option: ConversionBehavior)
355-
-> Result<i64, ()> {
375+
-> Result<ConversionResult<i64>, ()> {
356376
convert_int_from_jsval(cx, val, option, ToInt64)
357377
}
358378
}
@@ -371,7 +391,7 @@ impl FromJSValConvertible for u64 {
371391
unsafe fn from_jsval(cx: *mut JSContext,
372392
val: HandleValue,
373393
option: ConversionBehavior)
374-
-> Result<u64, ()> {
394+
-> Result<ConversionResult<u64>, ()> {
375395
convert_int_from_jsval(cx, val, option, ToUint64)
376396
}
377397
}
@@ -387,9 +407,9 @@ impl ToJSValConvertible for f32 {
387407
// https://heycam.github.io/webidl/#es-float
388408
impl FromJSValConvertible for f32 {
389409
type Config = ();
390-
unsafe fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f32, ()> {
410+
unsafe fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<ConversionResult<f32>, ()> {
391411
let result = ToNumber(cx, val);
392-
result.map(|f| f as f32)
412+
result.map(|f| f as f32).map(ConversionResult::Success)
393413
}
394414
}
395415

@@ -404,8 +424,8 @@ impl ToJSValConvertible for f64 {
404424
// https://heycam.github.io/webidl/#es-double
405425
impl FromJSValConvertible for f64 {
406426
type Config = ();
407-
unsafe fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f64, ()> {
408-
ToNumber(cx, val)
427+
unsafe fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<ConversionResult<f64>, ()> {
428+
ToNumber(cx, val).map(ConversionResult::Success)
409429
}
410430
}
411431

@@ -451,21 +471,21 @@ impl ToJSValConvertible for String {
451471
// https://heycam.github.io/webidl/#es-USVString
452472
impl FromJSValConvertible for String {
453473
type Config = ();
454-
unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<String, ()> {
474+
unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<ConversionResult<String>, ()> {
455475
let jsstr = ToString(cx, value);
456476
if jsstr.is_null() {
457477
debug!("ToString failed");
458478
return Err(());
459479
}
460480
if JS_StringHasLatin1Chars(jsstr) {
461-
return Ok(latin1_to_string(cx, jsstr));
481+
return Ok(latin1_to_string(cx, jsstr)).map(ConversionResult::Success);
462482
}
463483

464484
let mut length = 0;
465485
let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length);
466486
assert!(!chars.is_null());
467487
let char_vec = slice::from_raw_parts(chars, length as usize);
468-
Ok(String::from_utf16_lossy(char_vec))
488+
Ok(String::from_utf16_lossy(char_vec)).map(ConversionResult::Success)
469489
}
470490
}
471491

@@ -491,12 +511,14 @@ impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
491511
unsafe fn from_jsval(cx: *mut JSContext,
492512
value: HandleValue,
493513
option: T::Config)
494-
-> Result<Option<T>, ()> {
514+
-> Result<ConversionResult<Option<T>>, ()> {
495515
if value.get().is_null_or_undefined() {
496-
Ok(None)
516+
Ok(ConversionResult::Success(None))
497517
} else {
498-
let result: Result<T, ()> = FromJSValConvertible::from_jsval(cx, value, option);
499-
result.map(Some)
518+
Ok(match try!(FromJSValConvertible::from_jsval(cx, value, option)) {
519+
ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
520+
ConversionResult::Failure(v) => ConversionResult::Failure(v),
521+
})
500522
}
501523
}
502524
}
@@ -553,7 +575,7 @@ impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T
553575
unsafe fn from_jsval(cx: *mut JSContext,
554576
value: HandleValue,
555577
option: C)
556-
-> Result<Vec<T>, ()> {
578+
-> Result<ConversionResult<Vec<T>>, ()> {
557579
let mut iterator = ForOfIterator {
558580
cx_: cx,
559581
iterator: RootedObject::new_unrooted(ptr::null_mut()),
@@ -579,10 +601,13 @@ impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T
579601
break;
580602
}
581603

582-
ret.push(try!(T::from_jsval(cx, val.handle(), option.clone())));
604+
ret.push(match try!(T::from_jsval(cx, val.handle(), option.clone())) {
605+
ConversionResult::Success(v) => v,
606+
ConversionResult::Failure(e) => return Ok(ConversionResult::Failure(e)),
607+
});
583608
}
584609

585-
Ok(ret)
610+
Ok(ret).map(ConversionResult::Success)
586611
}
587612
}
588613

tests/vec_conversion.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ fn vec_conversion() {
4141
orig_vec.to_jsval(cx, rval.handle_mut());
4242
let converted = Vec::<f32>::from_jsval(cx, rval.handle(), ()).unwrap();
4343

44-
assert_eq!(orig_vec, converted);
44+
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
4545

4646
let orig_vec: Vec<i32> = vec![1, 2, 3];
4747
orig_vec.to_jsval(cx, rval.handle_mut());
4848
let converted = Vec::<i32>::from_jsval(cx, rval.handle(),
4949
ConversionBehavior::Default).unwrap();
5050

51-
assert_eq!(orig_vec, converted);
51+
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
5252

5353
rt.evaluate_script(global, "new Set([1, 2, 3])",
5454
"test", 1, rval.handle_mut()).unwrap();
5555
let converted =
5656
Vec::<i32>::from_jsval(cx, rval.handle(),
5757
ConversionBehavior::Default).unwrap();
5858

59-
assert_eq!(orig_vec, converted);
59+
assert_eq!(&orig_vec, converted.get_success_value().unwrap());
6060
}
6161
}

0 commit comments

Comments
 (0)