summaryrefslogtreecommitdiffstats
path: root/vendor/wasm-bindgen/src/externref.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wasm-bindgen/src/externref.rs')
-rw-r--r--vendor/wasm-bindgen/src/externref.rs184
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() {}