Merge pull request #955 from alexcrichton/non-send

Ensure that `JsValue` isn't considered `Send`
This commit is contained in:
Alex Crichton 2018-10-10 17:41:49 -07:00 committed by GitHub
commit e03e40451e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 56 deletions

View File

@ -1042,11 +1042,9 @@ impl ToTokens for ast::ImportStatic {
fn init() -> #ty { fn init() -> #ty {
panic!("cannot access imported statics on non-wasm targets") panic!("cannot access imported statics on non-wasm targets")
} }
static mut _VAL: ::wasm_bindgen::__rt::core::cell::UnsafeCell<Option<#ty>> = thread_local!(static _VAL: #ty = init(););
::wasm_bindgen::__rt::core::cell::UnsafeCell::new(None);
::wasm_bindgen::JsStatic { ::wasm_bindgen::JsStatic {
__inner: unsafe { &_VAL }, __inner: &_VAL,
__init: init,
} }
}; };
}).to_tokens(into); }).to_tokens(into);

View File

@ -214,7 +214,7 @@ impl<T> Closure<T>
}; };
Closure { Closure {
js: ManuallyDrop::new(JsValue { idx }), js: ManuallyDrop::new(JsValue::_new(idx)),
data: ManuallyDrop::new(data), data: ManuallyDrop::new(data),
} }
} }

View File

@ -337,7 +337,7 @@ impl FromWasmAbi for JsValue {
#[inline] #[inline]
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue { unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue {
JsValue { idx: js } JsValue::_new(js)
} }
} }
@ -356,7 +356,7 @@ impl RefFromWasmAbi for JsValue {
#[inline] #[inline]
unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor { unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor {
ManuallyDrop::new(JsValue { idx: js }) ManuallyDrop::new(JsValue::_new(js))
} }
} }

View File

@ -16,8 +16,8 @@ extern crate serde_json;
extern crate wasm_bindgen_macro; extern crate wasm_bindgen_macro;
use core::cell::UnsafeCell;
use core::fmt; use core::fmt;
use core::marker;
use core::mem; use core::mem;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::ptr; use core::ptr;
@ -64,6 +64,7 @@ if_std! {
/// but for now it may be slightly slow. /// but for now it may be slightly slow.
pub struct JsValue { pub struct JsValue {
idx: u32, idx: u32,
_marker: marker::PhantomData<*mut u8>, // not at all threadsafe
} }
const JSIDX_UNDEFINED: u32 = 0; const JSIDX_UNDEFINED: u32 = 0;
@ -74,18 +75,36 @@ const JSIDX_RESERVED: u32 = 8;
impl JsValue { impl JsValue {
/// The `null` JS value constant. /// The `null` JS value constant.
pub const NULL: JsValue = JsValue { idx: JSIDX_NULL }; pub const NULL: JsValue = JsValue {
idx: JSIDX_NULL,
_marker: marker::PhantomData,
};
/// The `undefined` JS value constant. /// The `undefined` JS value constant.
pub const UNDEFINED: JsValue = JsValue { pub const UNDEFINED: JsValue = JsValue {
idx: JSIDX_UNDEFINED, idx: JSIDX_UNDEFINED,
_marker: marker::PhantomData,
}; };
/// The `true` JS value constant. /// The `true` JS value constant.
pub const TRUE: JsValue = JsValue { idx: JSIDX_TRUE }; pub const TRUE: JsValue = JsValue {
idx: JSIDX_TRUE,
_marker: marker::PhantomData,
};
/// The `false` JS value constant. /// The `false` JS value constant.
pub const FALSE: JsValue = JsValue { idx: JSIDX_FALSE }; pub const FALSE: JsValue = JsValue {
idx: JSIDX_FALSE,
_marker: marker::PhantomData,
};
fn _new(idx: u32) -> JsValue {
JsValue {
idx,
_marker: marker::PhantomData,
}
}
/// Creates a new JS value which is a string. /// Creates a new JS value which is a string.
/// ///
@ -94,9 +113,7 @@ impl JsValue {
#[inline] #[inline]
pub fn from_str(s: &str) -> JsValue { pub fn from_str(s: &str) -> JsValue {
unsafe { unsafe {
JsValue { JsValue::_new(__wbindgen_string_new(s.as_ptr(), s.len()))
idx: __wbindgen_string_new(s.as_ptr(), s.len()),
}
} }
} }
@ -107,9 +124,7 @@ impl JsValue {
#[inline] #[inline]
pub fn from_f64(n: f64) -> JsValue { pub fn from_f64(n: f64) -> JsValue {
unsafe { unsafe {
JsValue { JsValue::_new(__wbindgen_number_new(n))
idx: __wbindgen_number_new(n),
}
} }
} }
@ -142,9 +157,7 @@ impl JsValue {
unsafe { unsafe {
let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null()); let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null());
let len = description.map(|s| s.len()).unwrap_or(0); let len = description.map(|s| s.len()).unwrap_or(0);
JsValue { JsValue::_new(__wbindgen_symbol_new(ptr, len))
idx: __wbindgen_symbol_new(ptr, len),
}
} }
} }
@ -170,9 +183,7 @@ impl JsValue {
{ {
let s = serde_json::to_string(t)?; let s = serde_json::to_string(t)?;
unsafe { unsafe {
Ok(JsValue { Ok(JsValue::_new(__wbindgen_json_parse(s.as_ptr(), s.len())))
idx: __wbindgen_json_parse(s.as_ptr(), s.len()),
})
} }
} }
@ -486,7 +497,7 @@ impl Clone for JsValue {
fn clone(&self) -> JsValue { fn clone(&self) -> JsValue {
unsafe { unsafe {
let idx = __wbindgen_object_clone_ref(self.idx); let idx = __wbindgen_object_clone_ref(self.idx);
JsValue { idx } JsValue::_new(idx)
} }
} }
} }
@ -552,43 +563,26 @@ impl Drop for JsValue {
/// ///
/// This type implements `Deref` to the inner type so it's typically used as if /// This type implements `Deref` to the inner type so it's typically used as if
/// it were `&T`. /// it were `&T`.
#[cfg(feature = "std")]
pub struct JsStatic<T: 'static> { pub struct JsStatic<T: 'static> {
#[doc(hidden)] #[doc(hidden)]
pub __inner: &'static UnsafeCell<Option<T>>, pub __inner: &'static std::thread::LocalKey<T>,
#[doc(hidden)]
pub __init: fn() -> T,
} }
unsafe impl<T: Sync> Sync for JsStatic<T> {} #[cfg(feature = "std")]
unsafe impl<T: Send> Send for JsStatic<T> {}
impl<T: FromWasmAbi + 'static> Deref for JsStatic<T> { impl<T: FromWasmAbi + 'static> Deref for JsStatic<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
// We know that our tls key is never overwritten after initialization,
// so it should be safe (on that axis at least) to hand out a reference
// that lives longer than the closure below.
//
// FIXME: this is not sound if we ever implement thread exit hooks on
// wasm, as the pointer will eventually be invalidated but you can get
// `&'static T` from this interface. We... probably need to deprecate
// and/or remove this interface nowadays.
unsafe { unsafe {
// Ideally we want to use `get_or_insert_with` here but self.__inner.with(|ptr| &*(ptr as *const T))
// unfortunately that has subpar codegen for now.
//
// If we get past the `Some` branch here LLVM statically
// knows that we're `None`, but the after the call to the `__init`
// function LLVM can no longer know this because `__init` could
// recursively call this function again (aka if JS came back to Rust
// and Rust referenced this static).
//
// We know, however, that cannot happen. As a result we can
// conclude that even after the call to `__init` our `ptr` still
// points to `None` (and a debug assertion to this effect). Then
// using `ptr::write` should tell rustc to not run destuctors
// (as one isn't there) and this should tighten up codegen for
// `JsStatic` a bit as well.
let ptr = self.__inner.get();
if let Some(ref t) = *ptr {
return t;
}
let init = Some((self.__init)());
debug_assert!((*ptr).is_none());
ptr::write(ptr, init);
(*ptr).as_ref().unwrap()
} }
} }
} }
@ -642,9 +636,7 @@ pub fn throw_val(s: JsValue) -> ! {
/// Returns a handle to this wasm instance's `WebAssembly.Memory` /// Returns a handle to this wasm instance's `WebAssembly.Memory`
pub fn memory() -> JsValue { pub fn memory() -> JsValue {
unsafe { unsafe {
JsValue { JsValue::_new(__wbindgen_memory())
idx: __wbindgen_memory(),
}
} }
} }