ion/conversions/value/
from.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use std::ptr;
8
9pub use mozjs::conversions::ConversionBehavior;
10use mozjs::conversions::{ConversionResult, FromJSValConvertible as _};
11use mozjs::jsapi::{
12	AssertSameCompartment, AssertSameCompartment1, ForOfIterator, ForOfIterator_NonIterableBehavior, JSFunction,
13	JSObject, JSString, RootedObject, RootedValue, Symbol as JSSymbol,
14};
15use mozjs::jsval::{JSVal, UndefinedValue};
16use mozjs::rust::{ToBoolean, ToNumber, ToString};
17use mozjs::typedarray as jsta;
18use mozjs::typedarray::JSObjectStorage;
19
20use crate::object::RegExp;
21use crate::string::byte::{BytePredicate, ByteString};
22use crate::typedarray::{ArrayBuffer, TypedArray, TypedArrayElement};
23use crate::{
24	Array, Context, Date, Error, ErrorKind, Exception, Function, Object, Promise, Result, StringRef, Symbol, Value,
25};
26
27/// Represents types that can be converted to from [JavaScript Values](Value).
28pub trait FromValue<'cx>: Sized {
29	type Config;
30
31	/// Converts `value` to the desired type.
32	/// `strict` and `config` determine the strictness of the conversion and specify additional conversion constraints respectively.
33	/// Returns [Err] with the [error](Error) if conversion fails.
34	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: Self::Config) -> Result<Self>;
35}
36
37impl<'cx> FromValue<'cx> for bool {
38	type Config = ();
39
40	fn from_value(_: &'cx Context, value: &Value, strict: bool, _: ()) -> Result<bool> {
41		let value = value.handle();
42		if value.is_boolean() {
43			return Ok(value.to_boolean());
44		}
45
46		if strict {
47			Err(Error::new("Expected Boolean in Strict Conversion", ErrorKind::Type))
48		} else {
49			Ok(unsafe { ToBoolean(value) })
50		}
51	}
52}
53
54macro_rules! impl_from_value_for_integer {
55	($ty:ty) => {
56		impl<'cx> FromValue<'cx> for $ty {
57			type Config = ConversionBehavior;
58
59			fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ConversionBehavior) -> Result<$ty> {
60				let value = value.handle();
61				if strict && !value.is_number() {
62					return Err(Error::new(
63						"Expected Number in Strict Conversion",
64						ErrorKind::Type,
65					));
66				}
67
68				match unsafe { <$ty>::from_jsval(cx.as_ptr(), value, config) } {
69					Ok(ConversionResult::Success(number)) => Ok(number),
70					Err(_) => Err(Exception::new(cx)?.unwrap().to_error()),
71					_ => unreachable!(),
72				}
73			}
74		}
75	};
76}
77
78impl_from_value_for_integer!(u8);
79impl_from_value_for_integer!(u16);
80impl_from_value_for_integer!(u32);
81impl_from_value_for_integer!(u64);
82
83impl_from_value_for_integer!(i8);
84impl_from_value_for_integer!(i16);
85impl_from_value_for_integer!(i32);
86impl_from_value_for_integer!(i64);
87
88impl<'cx> FromValue<'cx> for f32 {
89	type Config = ();
90
91	fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> Result<f32> {
92		f64::from_value(cx, value, strict, ()).map(|float| float as f32)
93	}
94}
95
96impl<'cx> FromValue<'cx> for f64 {
97	type Config = ();
98
99	fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> Result<f64> {
100		let value = value.handle();
101		if strict && !value.is_number() {
102			return Err(Error::new("Expected Number in Strict Conversion", ErrorKind::Type));
103		}
104
105		let number = unsafe { ToNumber(cx.as_ptr(), value) };
106		number.map_err(|_| Error::new("Unable to Convert Value to Number", ErrorKind::Type))
107	}
108}
109
110impl<'cx> FromValue<'cx> for *mut JSString {
111	type Config = ();
112
113	fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> Result<*mut JSString> {
114		let value = value.handle();
115		if strict && !value.is_string() {
116			return Err(Error::new("Expected String in Strict Conversion", ErrorKind::Type));
117		}
118
119		let str = unsafe { ToString(cx.as_ptr(), value) };
120		if str.is_null() {
121			Err(Error::new("Failed to convert value to String", ErrorKind::Type))
122		} else {
123			Ok(str)
124		}
125	}
126}
127
128impl<'cx> FromValue<'cx> for crate::String<'cx> {
129	type Config = ();
130
131	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ()) -> Result<crate::String<'cx>> {
132		<*mut JSString>::from_value(cx, value, strict, config).map(|str| crate::String::from(cx.root(str)))
133	}
134}
135
136impl<'cx> FromValue<'cx> for StringRef<'cx> {
137	type Config = ();
138
139	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ()) -> Result<StringRef<'cx>> {
140		crate::String::from_value(cx, value, strict, config).map(|str| str.as_ref(cx))
141	}
142}
143
144impl<'cx, T: BytePredicate> FromValue<'cx> for ByteString<T> {
145	type Config = ();
146
147	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ()) -> Result<ByteString<T>> {
148		const INVALID_CHARACTERS: &str = "ByteString contains invalid characters";
149		let string = StringRef::from_value(cx, value, strict, config)?;
150		match string {
151			StringRef::Latin1(bstr) => {
152				ByteString::from(bstr.to_vec()).ok_or_else(|| Error::new(INVALID_CHARACTERS, ErrorKind::Type))
153			}
154			StringRef::Utf16(wstr) => {
155				let bytes = wstr
156					.as_bytes()
157					.chunks_exact(2)
158					.map(|chunk| {
159						let codepoint = u16::from_ne_bytes([chunk[0], chunk[1]]);
160						u8::try_from(codepoint).map_err(|_| Error::new(INVALID_CHARACTERS, ErrorKind::Type))
161					})
162					.collect::<Result<Vec<_>>>()?;
163				ByteString::from(bytes).ok_or_else(|| Error::new(INVALID_CHARACTERS, ErrorKind::Type))
164			}
165		}
166	}
167}
168
169impl<'cx> FromValue<'cx> for String {
170	type Config = ();
171
172	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ()) -> Result<String> {
173		let str = crate::String::from_value(cx, value, strict, config)?;
174		str.to_owned(cx)
175	}
176}
177
178impl<'cx> FromValue<'cx> for *mut JSObject {
179	type Config = ();
180
181	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<*mut JSObject> {
182		let value = value.handle();
183		if !value.is_object() {
184			return Err(Error::new("Expected Object", ErrorKind::Type));
185		}
186		let object = value.to_object();
187		unsafe {
188			AssertSameCompartment(cx.as_ptr(), object);
189		}
190
191		Ok(object)
192	}
193}
194
195impl<'cx> FromValue<'cx> for Object<'cx> {
196	type Config = ();
197
198	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Object<'cx>> {
199		if !value.handle().is_object() {
200			return Err(Error::new("Expected Object", ErrorKind::Type));
201		}
202		let object = value.to_object(cx);
203		unsafe {
204			AssertSameCompartment(cx.as_ptr(), object.handle().get());
205		}
206
207		Ok(object)
208	}
209}
210
211impl<'cx> FromValue<'cx> for Array<'cx> {
212	type Config = ();
213
214	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Array<'cx>> {
215		if !value.handle().is_object() {
216			return Err(Error::new("Expected Array", ErrorKind::Type));
217		}
218
219		let object = value.to_object(cx).into_local();
220		if let Some(array) = Array::from(cx, object) {
221			unsafe {
222				AssertSameCompartment(cx.as_ptr(), array.handle().get());
223			}
224			Ok(array)
225		} else {
226			Err(Error::new("Expected Array", ErrorKind::Type))
227		}
228	}
229}
230
231impl<'cx> FromValue<'cx> for Date<'cx> {
232	type Config = ();
233
234	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Date<'cx>> {
235		if !value.handle().is_object() {
236			return Err(Error::new("Expected Date", ErrorKind::Type));
237		}
238
239		let object = value.to_object(cx).into_local();
240		if let Some(date) = Date::from(cx, object) {
241			unsafe {
242				AssertSameCompartment(cx.as_ptr(), date.get());
243			}
244			Ok(date)
245		} else {
246			Err(Error::new("Expected Date", ErrorKind::Type))
247		}
248	}
249}
250
251impl<'cx> FromValue<'cx> for Promise<'cx> {
252	type Config = ();
253
254	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Promise<'cx>> {
255		if !value.handle().is_object() {
256			return Err(Error::new("Expected Promise", ErrorKind::Type));
257		}
258
259		let object = value.to_object(cx).into_local();
260		if let Some(promise) = Promise::from(object) {
261			unsafe {
262				AssertSameCompartment(cx.as_ptr(), promise.get());
263			}
264			Ok(promise)
265		} else {
266			Err(Error::new("Expected Promise", ErrorKind::Type))
267		}
268	}
269}
270
271impl<'cx> FromValue<'cx> for RegExp<'cx> {
272	type Config = ();
273
274	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<RegExp<'cx>> {
275		if !value.handle().is_object() {
276			return Err(Error::new("Expected RegExp", ErrorKind::Type));
277		}
278
279		let object = value.to_object(cx).into_local();
280		if let Some(regexp) = RegExp::from(cx, object) {
281			unsafe {
282				AssertSameCompartment(cx.as_ptr(), regexp.get());
283			}
284			Ok(regexp)
285		} else {
286			Err(Error::new("Expected RegExp", ErrorKind::Type))
287		}
288	}
289}
290
291impl<'cx, T: jsta::TypedArrayElement, S: JSObjectStorage> FromValue<'cx> for jsta::TypedArray<T, S> {
292	type Config = ();
293
294	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<jsta::TypedArray<T, S>> {
295		let value = value.handle();
296		if value.is_object() {
297			let object = value.to_object();
298			cx.root(object);
299			jsta::TypedArray::from(object).map_err(|_| Error::new("Expected Typed Array", ErrorKind::Type))
300		} else {
301			Err(Error::new("Expected Object", ErrorKind::Type))
302		}
303	}
304}
305
306impl<'cx> FromValue<'cx> for ArrayBuffer<'cx> {
307	type Config = ();
308
309	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<ArrayBuffer<'cx>> {
310		if !value.handle().is_object() {
311			return Err(Error::new("Expected ArrayBuffer", ErrorKind::Type));
312		}
313
314		let object = value.to_object(cx).into_local();
315		if let Some(buffer) = ArrayBuffer::from(object) {
316			unsafe {
317				AssertSameCompartment(cx.as_ptr(), buffer.get());
318			}
319			Ok(buffer)
320		} else {
321			Err(Error::new("Expected ArrayBuffer", ErrorKind::Type))
322		}
323	}
324}
325
326impl<'cx, T: TypedArrayElement> FromValue<'cx> for TypedArray<'cx, T> {
327	type Config = ();
328
329	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<TypedArray<'cx, T>> {
330		if !value.handle().is_object() {
331			return Err(Error::new("Expected ArrayBuffer", ErrorKind::Type));
332		}
333
334		let object = value.to_object(cx).into_local();
335		if let Some(array) = TypedArray::from(object) {
336			unsafe {
337				AssertSameCompartment(cx.as_ptr(), array.get());
338			}
339			Ok(array)
340		} else {
341			Err(Error::new("Expected ArrayBuffer", ErrorKind::Type))
342		}
343	}
344}
345
346impl<'cx> FromValue<'cx> for *mut JSFunction {
347	type Config = ();
348
349	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: ()) -> Result<*mut JSFunction> {
350		Function::from_value(cx, value, strict, config).map(|f| f.get())
351	}
352}
353
354impl<'cx> FromValue<'cx> for Function<'cx> {
355	type Config = ();
356
357	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Function<'cx>> {
358		if !value.handle().is_object() {
359			return Err(Error::new("Expected Function", ErrorKind::Type));
360		}
361
362		let function_obj = value.to_object(cx);
363		if let Some(function) = Function::from_object(cx, &function_obj) {
364			unsafe {
365				AssertSameCompartment(cx.as_ptr(), function_obj.handle().get());
366			}
367			Ok(function)
368		} else {
369			Err(Error::new("Expected Function", ErrorKind::Type))
370		}
371	}
372}
373
374impl<'cx> FromValue<'cx> for *mut JSSymbol {
375	type Config = ();
376
377	fn from_value(_: &'cx Context, value: &Value, _: bool, _: ()) -> Result<*mut JSSymbol> {
378		let value = value.handle();
379		if value.is_symbol() {
380			Ok(value.to_symbol())
381		} else {
382			Err(Error::new("Expected Symbol", ErrorKind::Type))
383		}
384	}
385}
386
387impl<'cx> FromValue<'cx> for Symbol<'cx> {
388	type Config = ();
389
390	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: Self::Config) -> Result<Symbol<'cx>> {
391		<*mut JSSymbol>::from_value(cx, value, strict, config).map(|s| cx.root(s).into())
392	}
393}
394
395impl<'cx> FromValue<'cx> for JSVal {
396	type Config = ();
397
398	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<JSVal> {
399		let value = value.handle();
400		unsafe {
401			AssertSameCompartment1(cx.as_ptr(), value.into());
402		}
403		Ok(value.get())
404	}
405}
406
407impl<'cx> FromValue<'cx> for Value<'cx> {
408	type Config = ();
409
410	fn from_value(cx: &'cx Context, value: &Value, _: bool, _: ()) -> Result<Value<'cx>> {
411		let value = value.handle();
412		unsafe {
413			AssertSameCompartment1(cx.as_ptr(), value.into());
414		}
415		Ok(cx.root(value.get()).into())
416	}
417}
418
419impl<'cx, T: FromValue<'cx>> FromValue<'cx> for Option<T> {
420	type Config = T::Config;
421
422	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: T::Config) -> Result<Option<T>> {
423		if value.handle().is_null_or_undefined() {
424			Ok(None)
425		} else {
426			Ok(Some(T::from_value(cx, value, strict, config)?))
427		}
428	}
429}
430
431// Copied from [rust-mozjs](https://github.com/servo/rust-mozjs/blob/master/src/conversions.rs#L619-L642)
432struct ForOfIteratorGuard<'a> {
433	root: &'a mut ForOfIterator,
434}
435
436impl<'a> ForOfIteratorGuard<'a> {
437	fn new(cx: &Context, root: &'a mut ForOfIterator) -> Self {
438		cx.root(root.iterator.data);
439		ForOfIteratorGuard { root }
440	}
441}
442
443impl<'cx, T: FromValue<'cx>> FromValue<'cx> for Vec<T>
444where
445	T::Config: Clone,
446{
447	type Config = T::Config;
448
449	// Adapted from [rust-mozjs](https://github.com/servo/rust-mozjs/blob/master/src/conversions.rs#L644-L707)
450	fn from_value(cx: &'cx Context, value: &Value, strict: bool, config: T::Config) -> Result<Vec<T>> {
451		if !value.handle().is_object() {
452			return Err(Error::new("Expected Object", ErrorKind::Type));
453		}
454		let object = value.to_object(cx);
455		if strict && !Array::is_array(cx, &object) {
456			return Err(Error::new("Expected Array", ErrorKind::Type));
457		}
458
459		let mut iterator = ForOfIterator {
460			cx_: cx.as_ptr(),
461			iterator: RootedObject::new_unrooted(ptr::null_mut()),
462			nextMethod: RootedValue::new_unrooted(UndefinedValue()),
463			index: u32::MAX, // NOT_ARRAY
464		};
465		let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
466		let iterator = &mut *iterator.root;
467
468		let init = unsafe {
469			iterator.init(
470				value.handle().into(),
471				ForOfIterator_NonIterableBehavior::AllowNonIterable,
472			)
473		};
474		if !init {
475			return Err(Error::new("Failed to Initialise Iterator", ErrorKind::Type));
476		}
477
478		if iterator.iterator.data.is_null() {
479			return Err(Error::new("Expected Iterable", ErrorKind::Type));
480		}
481
482		let mut ret = vec![];
483
484		let mut value = Value::undefined(cx);
485		loop {
486			let mut done = false;
487			if unsafe { !iterator.next(value.handle_mut().into(), &raw mut done) } {
488				return Err(Error::new("Failed to Execute Next on Iterator", ErrorKind::Type));
489			}
490
491			if done {
492				break;
493			}
494			ret.push(T::from_value(cx, &value, strict, config.clone())?);
495		}
496		Ok(ret)
497	}
498}
499
500#[cfg(test)]
501mod tests {
502	use std::f64::consts::PI;
503
504	use chrono::{TimeZone as _, Utc};
505	use mozjs::conversions::ConversionBehavior;
506	use mozjs::gc::{RootableVec, RootedVec};
507	use mozjs::jsval::Int32Value;
508
509	use crate::conversions::{FromValue as _, ToValue as _};
510	use crate::utils::test::TestRuntime;
511	use crate::{Array, Date, Object, Promise, Value};
512
513	#[test]
514	fn boolean() {
515		let rt = TestRuntime::new();
516		let cx = &rt.cx;
517
518		let value = Value::bool(cx, false);
519		let result = bool::from_value(cx, &value, true, ());
520		assert!(!result.unwrap());
521
522		let value = Value::i32(cx, 0);
523		let result = bool::from_value(cx, &value, true, ());
524		result.unwrap_err();
525		let result = bool::from_value(cx, &value, false, ());
526		assert!(!result.unwrap());
527
528		let value = Value::f64(cx, PI);
529		let result = bool::from_value(cx, &value, true, ());
530		result.unwrap_err();
531		let result = bool::from_value(cx, &value, false, ());
532		assert!(result.unwrap());
533
534		let value = Value::string(cx, "");
535		let result = bool::from_value(cx, &value, true, ());
536		result.unwrap_err();
537		let result = bool::from_value(cx, &value, false, ());
538		assert!(!result.unwrap());
539
540		let value = Value::string(cx, "spider");
541		let result = bool::from_value(cx, &value, true, ());
542		result.unwrap_err();
543		let result = bool::from_value(cx, &value, false, ());
544		assert!(result.unwrap());
545
546		let value = Value::undefined(cx);
547		let result = bool::from_value(cx, &value, true, ());
548		result.unwrap_err();
549		let result = bool::from_value(cx, &value, false, ());
550		assert!(!result.unwrap());
551
552		let value = Value::null(cx);
553		let result = bool::from_value(cx, &value, true, ());
554		result.unwrap_err();
555		let result = bool::from_value(cx, &value, false, ());
556		assert!(!result.unwrap());
557
558		let object = Object::new(cx);
559		let value = object.as_value(cx);
560		let result = bool::from_value(cx, &value, true, ());
561		result.unwrap_err();
562		let result = bool::from_value(cx, &value, false, ());
563		assert!(result.unwrap());
564
565		let array = Array::new(cx);
566		let value = array.as_value(cx);
567		let result = bool::from_value(cx, &value, true, ());
568		result.unwrap_err();
569		let result = bool::from_value(cx, &value, false, ());
570		assert!(result.unwrap());
571	}
572
573	#[test]
574	fn integer() {
575		let rt = TestRuntime::new();
576		let cx = &rt.cx;
577
578		let value = Value::bool(cx, true);
579		let result = i32::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
580		result.unwrap_err();
581		let result = i32::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
582		assert_eq!(result.unwrap(), 1);
583
584		let value = Value::i32(cx, 255);
585		let result = u8::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
586		assert_eq!(result.unwrap(), 255);
587
588		let value = Value::string(cx, "spider");
589		let result = u16::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
590		result.unwrap_err();
591		let result = u16::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
592		result.unwrap_err();
593
594		let value = Value::string(cx, "-64");
595		let result = i64::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
596		result.unwrap_err();
597		let result = i64::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
598		assert_eq!(result.unwrap(), -64);
599
600		let value = Value::undefined(cx);
601		let result = i64::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
602		result.unwrap_err();
603		let result = i64::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
604		result.unwrap_err();
605
606		let value = Value::null(cx);
607		let result = i64::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
608		result.unwrap_err();
609		let result = i64::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
610		assert_eq!(result.unwrap(), 0);
611
612		let object = Object::new(cx);
613		let value = object.as_value(cx);
614		let result = u32::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
615		result.unwrap_err();
616		let result = u32::from_value(cx, &value, false, ConversionBehavior::EnforceRange);
617		result.unwrap_err();
618	}
619
620	#[test]
621	fn string() {
622		let rt = TestRuntime::new();
623		let cx = &rt.cx;
624
625		let value = Value::bool(cx, false);
626		let result = String::from_value(cx, &value, true, ());
627		result.unwrap_err();
628		let result = String::from_value(cx, &value, false, ());
629		assert_eq!(&result.unwrap(), "false");
630
631		let value = Value::f64(cx, 1.5);
632		let result = String::from_value(cx, &value, true, ());
633		result.unwrap_err();
634		let result = String::from_value(cx, &value, false, ());
635		assert_eq!(&result.unwrap(), "1.5");
636
637		let value = Value::string(cx, "spider");
638		let result = String::from_value(cx, &value, true, ());
639		assert_eq!(&result.unwrap(), "spider");
640
641		let value = Value::undefined(cx);
642		let result = String::from_value(cx, &value, true, ());
643		result.unwrap_err();
644		let result = String::from_value(cx, &value, false, ());
645		assert_eq!(&result.unwrap(), "undefined");
646
647		let value = Value::null(cx);
648		let result = String::from_value(cx, &value, true, ());
649		result.unwrap_err();
650		let result = String::from_value(cx, &value, false, ());
651		assert_eq!(&result.unwrap(), "null");
652
653		let object = Object::new(cx);
654		let value = object.as_value(cx);
655		let result = String::from_value(cx, &value, true, ());
656		result.unwrap_err();
657		let result = String::from_value(cx, &value, false, ());
658		assert_eq!(&result.unwrap(), "[object Object]");
659	}
660
661	#[test]
662	fn object() {
663		let rt = TestRuntime::new();
664		let cx = &rt.cx;
665
666		let value = Value::bool(cx, false);
667		let result = Object::from_value(cx, &value, true, ());
668		result.unwrap_err();
669		let result = Object::from_value(cx, &value, false, ());
670		result.unwrap_err();
671
672		let value = Value::f64(cx, 144.0);
673		let result = Object::from_value(cx, &value, true, ());
674		result.unwrap_err();
675		let result = Object::from_value(cx, &value, false, ());
676		result.unwrap_err();
677
678		let value = Value::string(cx, "spider");
679		let result = Object::from_value(cx, &value, true, ());
680		result.unwrap_err();
681		let result = Object::from_value(cx, &value, false, ());
682		result.unwrap_err();
683
684		let value = Value::undefined(cx);
685		let result = Object::from_value(cx, &value, true, ());
686		result.unwrap_err();
687		let result = Object::from_value(cx, &value, false, ());
688		result.unwrap_err();
689
690		let value = Value::null(cx);
691		let result = Object::from_value(cx, &value, true, ());
692		result.unwrap_err();
693		let result = Object::from_value(cx, &value, false, ());
694		result.unwrap_err();
695
696		let object = Object::new(cx);
697		let value = object.as_value(cx);
698		let result = Object::from_value(cx, &value, true, ());
699		result.unwrap();
700
701		let array = Array::new(cx);
702		let value = array.as_value(cx);
703		let result = Array::from_value(cx, &value, true, ());
704		result.unwrap();
705
706		let timestamp = Utc.timestamp_millis_opt(Utc::now().timestamp_millis()).unwrap();
707		let date = Date::from_date(cx, timestamp);
708		let value = date.as_value(cx);
709		let result = Date::from_value(cx, &value, true, ());
710		assert_eq!(result.unwrap().to_date(cx).unwrap(), timestamp);
711
712		let promise = Promise::new(cx);
713		let value = promise.as_value(cx);
714		let result = Promise::from_value(cx, &value, true, ());
715		result.unwrap();
716	}
717
718	#[test]
719	fn option() {
720		type Opt = Option<bool>;
721
722		let rt = TestRuntime::new();
723		let cx = &rt.cx;
724
725		let value = Value::bool(cx, true);
726		let result = Opt::from_value(cx, &value, true, ());
727		assert_eq!(result.unwrap(), Some(true));
728
729		let value = Value::undefined(cx);
730		let result = Opt::from_value(cx, &value, true, ());
731		assert_eq!(result.unwrap(), None);
732
733		let value = Value::null(cx);
734		let result = Opt::from_value(cx, &value, true, ());
735		assert_eq!(result.unwrap(), None);
736	}
737
738	#[test]
739	fn vec() {
740		let rt = TestRuntime::new();
741		let cx = &rt.cx;
742
743		let int_vec = vec![1, 256, -0x10000, 0x7FFF_FFFF];
744		let mut root = RootableVec::new_unrooted();
745		let vec = RootedVec::from_iter(&mut root, int_vec.iter().map(|i| Int32Value(*i)));
746		let array = Array::from_rooted_vec(cx, &vec);
747		let value = array.as_value(cx);
748
749		let result = <Vec<i32>>::from_value(cx, &value, true, ConversionBehavior::EnforceRange);
750		assert_eq!(result.unwrap(), int_vec);
751	}
752}