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}