js_promises/
jsbox.rs

1//! Functions for passing arbitrary Rust values across the JS boundary and back to Rust, ex. as a return value from a promise.
2
3use stdweb::{
4	self,
5	js,
6	unstable::TryFrom,
7};
8
9/// Boxes an object, returning a JS wrapper that `js_unbox` can consume to turn back into the object.
10/// 
11/// The contents of the return value should be untouched, both by rust code and by JS code, for
12/// safe usage with `js_unbox`.
13pub fn js_box<T>(v: T) -> stdweb::Value {
14	let ptr = Box::into_raw(Box::new(v)) as usize;
15	let ptr_js = stdweb::Value::try_from(ptr).expect("Could not convert ptr to js number");
16	let v = js!{
17		return {"_ptr": @{ptr_js}};
18	};
19	v
20}
21
22/// Unboxes a value created by `js_box`, returning the original object.
23/// 
24/// Returns `Some(v)` if the unboxing succeeded, or `None` if the value was not a valid box object
25/// or it was already unboxed.
26/// 
27/// This mutates the object, so that calling `js_unbox` on the same object will return `None`.
28/// 
29/// Safety
30/// ------
31/// 
32/// Assumes that the object has been untouched since being returned from `js_box` or `js_box_from_std_box`,
33/// and that the type argument is the same as the one used to box it.
34/// 
35/// JS or Rust code could modify the object or construct a JS object with an invalid internal pointer,
36/// which this function can't check for. In such a case, the result is undefined.
37/// 
38/// If the value is sent across a thread, the caller needs to ensure that `T` implements `Send`
39/// (TODO: is this even possible with wasm?).
40/// 
41/// The lifetime of `T` must still be valid. If in doubt, require `'static`.
42pub unsafe fn js_unbox<T>(v: stdweb::Value) -> Option<T> {
43	let v_num_obj = v
44		.into_reference()?
45		.downcast::<stdweb::Object>()?
46		.to_iter()
47		.filter(|&(ref k, ref _v)| k == "_ptr")
48		.next()?
49		.1
50	;
51	
52	let v_num = match v_num_obj {
53		stdweb::Value::Number(n) => n,
54		_ => { return None; }
55	};
56	
57	let v_ptr = usize::try_from(v_num).ok()? as *mut T;
58	Some(*Box::from_raw(v_ptr))
59}