diff options
Diffstat (limited to 'vendor/wasm-bindgen/src/externref.rs')
-rw-r--r-- | vendor/wasm-bindgen/src/externref.rs | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/wasm-bindgen/src/externref.rs b/vendor/wasm-bindgen/src/externref.rs new file mode 100644 index 000000000..a7e5f3ebc --- /dev/null +++ b/vendor/wasm-bindgen/src/externref.rs @@ -0,0 +1,184 @@ +use crate::JsValue; +use std::alloc::{self, Layout}; +use std::cell::Cell; +use std::mem; +use std::ptr; +use std::slice; +use std::vec::Vec; +use std::cmp::max; + +externs! { + #[link(wasm_import_module = "__wbindgen_externref_xform__")] + extern "C" { + fn __wbindgen_externref_table_grow(delta: usize) -> i32; + fn __wbindgen_externref_table_set_null(idx: usize) -> (); + } +} + +pub struct Slab { + data: Vec<usize>, + head: usize, + base: usize, +} + +impl Slab { + fn new() -> Slab { + Slab { + data: Vec::new(), + head: 0, + base: 0, + } + } + + fn alloc(&mut self) -> usize { + let ret = self.head; + if ret == self.data.len() { + let curr_len = self.data.len(); + if curr_len == self.data.capacity() { + let extra = max(128, curr_len); + let r = unsafe { __wbindgen_externref_table_grow(extra) }; + if r == -1 { + internal_error("table grow failure") + } + if self.base == 0 { + self.base = r as usize; + } else if self.base + self.data.len() != r as usize { + internal_error("someone else allocated table entires?") + } + + // poor man's `try_reserve_exact` until that's stable + unsafe { + let new_cap = self.data.capacity() + extra; + let size = mem::size_of::<usize>() * new_cap; + let align = mem::align_of::<usize>(); + let layout = match Layout::from_size_align(size, align) { + Ok(l) => l, + Err(_) => internal_error("size/align layout failure"), + }; + let ptr = alloc::alloc(layout) as *mut usize; + if ptr.is_null() { + internal_error("allocation failure"); + } + ptr::copy_nonoverlapping(self.data.as_ptr(), ptr, self.data.len()); + let new_vec = Vec::from_raw_parts(ptr, self.data.len(), new_cap); + let mut old = mem::replace(&mut self.data, new_vec); + old.set_len(0); + } + } + + // custom condition to ensure `push` below doesn't call `reserve` in + // optimized builds which pulls in lots of panic infrastructure + if self.data.len() >= self.data.capacity() { + internal_error("push should be infallible now") + } + self.data.push(ret + 1); + } + + // usage of `get_mut` thwarts panicking infrastructure in optimized + // builds + match self.data.get_mut(ret) { + Some(slot) => self.head = *slot, + None => internal_error("ret out of bounds"), + } + ret + self.base + } + + fn dealloc(&mut self, slot: usize) { + if slot < self.base { + internal_error("free reserved slot"); + } + let slot = slot - self.base; + + // usage of `get_mut` thwarts panicking infrastructure in optimized + // builds + match self.data.get_mut(slot) { + Some(ptr) => { + *ptr = self.head; + self.head = slot; + } + None => internal_error("slot out of bounds"), + } + } + + fn live_count(&self) -> u32 { + let mut free_count = 0; + let mut next = self.head; + while next < self.data.len() { + debug_assert!((free_count as usize) < self.data.len()); + free_count += 1; + match self.data.get(next) { + Some(n) => next = *n, + None => internal_error("slot out of bounds"), + }; + } + self.data.len() as u32 - free_count + } +} + +fn internal_error(msg: &str) -> ! { + if cfg!(debug_assertions) { + super::throw_str(msg) + } else { + std::process::abort() + } +} + +// Management of `externref` is always thread local since an `externref` value +// can't cross threads in wasm. Indices as a result are always thread-local. +std::thread_local!(pub static HEAP_SLAB: Cell<Slab> = Cell::new(Slab::new())); + +#[no_mangle] +pub extern "C" fn __externref_table_alloc() -> usize { + HEAP_SLAB + .try_with(|slot| { + let mut slab = slot.replace(Slab::new()); + let ret = slab.alloc(); + slot.replace(slab); + ret + }) + .unwrap_or_else(|_| internal_error("tls access failure")) +} + +#[no_mangle] +pub extern "C" fn __externref_table_dealloc(idx: usize) { + if idx < super::JSIDX_RESERVED as usize { + return; + } + // clear this value from the table so while the table slot is un-allocated + // we don't keep around a strong reference to a potentially large object + unsafe { + __wbindgen_externref_table_set_null(idx); + } + HEAP_SLAB + .try_with(|slot| { + let mut slab = slot.replace(Slab::new()); + slab.dealloc(idx); + slot.replace(slab); + }) + .unwrap_or_else(|_| internal_error("tls access failure")) +} + +#[no_mangle] +pub unsafe extern "C" fn __externref_drop_slice(ptr: *mut JsValue, len: usize) { + for slot in slice::from_raw_parts_mut(ptr, len) { + __externref_table_dealloc(slot.idx as usize); + } +} + +// Implementation of `__wbindgen_externref_heap_live_count` for when we are using +// `externref` instead of the JS `heap`. +#[no_mangle] +pub unsafe extern "C" fn __externref_heap_live_count() -> u32 { + HEAP_SLAB + .try_with(|slot| { + let slab = slot.replace(Slab::new()); + let count = slab.live_count(); + slot.replace(slab); + count + }) + .unwrap_or_else(|_| internal_error("tls access failure")) +} + +// see comment in module above this in `link_mem_intrinsics` +#[inline(never)] +pub fn link_intrinsics() {} |