| 1 | use crate::JsValue; |
| 2 | |
| 3 | use alloc::slice; |
| 4 | use alloc::vec::Vec; |
| 5 | use core::cell::Cell; |
| 6 | use core::cmp::max; |
| 7 | |
| 8 | externs! { |
| 9 | #[link (wasm_import_module = "__wbindgen_externref_xform__" )] |
| 10 | extern "C" { |
| 11 | fn __wbindgen_externref_table_grow(delta: usize) -> i32; |
| 12 | fn __wbindgen_externref_table_set_null(idx: usize) -> (); |
| 13 | } |
| 14 | } |
| 15 | |
| 16 | pub struct Slab { |
| 17 | data: Vec<usize>, |
| 18 | head: usize, |
| 19 | base: usize, |
| 20 | } |
| 21 | |
| 22 | impl Slab { |
| 23 | fn new() -> Slab { |
| 24 | Slab { |
| 25 | data: Vec::new(), |
| 26 | head: 0, |
| 27 | base: 0, |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | fn alloc(&mut self) -> usize { |
| 32 | let ret = self.head; |
| 33 | if ret == self.data.len() { |
| 34 | let curr_len = self.data.len(); |
| 35 | if curr_len == self.data.capacity() { |
| 36 | let extra = max(128, curr_len); |
| 37 | let r = unsafe { __wbindgen_externref_table_grow(extra) }; |
| 38 | if r == -1 { |
| 39 | internal_error("table grow failure" ) |
| 40 | } |
| 41 | if self.base == 0 { |
| 42 | self.base = r as usize; |
| 43 | } else if self.base + self.data.len() != r as usize { |
| 44 | internal_error("someone else allocated table entries?" ) |
| 45 | } |
| 46 | |
| 47 | if self.data.try_reserve_exact(extra).is_err() { |
| 48 | internal_error("allocation failure" ); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | // custom condition to ensure `push` below doesn't call `reserve` in |
| 53 | // optimized builds which pulls in lots of panic infrastructure |
| 54 | if self.data.len() >= self.data.capacity() { |
| 55 | internal_error("push should be infallible now" ) |
| 56 | } |
| 57 | self.data.push(ret + 1); |
| 58 | } |
| 59 | |
| 60 | // usage of `get_mut` thwarts panicking infrastructure in optimized |
| 61 | // builds |
| 62 | match self.data.get_mut(ret) { |
| 63 | Some(slot) => self.head = *slot, |
| 64 | None => internal_error("ret out of bounds" ), |
| 65 | } |
| 66 | ret + self.base |
| 67 | } |
| 68 | |
| 69 | fn dealloc(&mut self, slot: usize) { |
| 70 | if slot < self.base { |
| 71 | internal_error("free reserved slot" ); |
| 72 | } |
| 73 | let slot = slot - self.base; |
| 74 | |
| 75 | // usage of `get_mut` thwarts panicking infrastructure in optimized |
| 76 | // builds |
| 77 | match self.data.get_mut(slot) { |
| 78 | Some(ptr) => { |
| 79 | *ptr = self.head; |
| 80 | self.head = slot; |
| 81 | } |
| 82 | None => internal_error("slot out of bounds" ), |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | fn live_count(&self) -> u32 { |
| 87 | let mut free_count = 0; |
| 88 | let mut next = self.head; |
| 89 | while next < self.data.len() { |
| 90 | debug_assert!((free_count as usize) < self.data.len()); |
| 91 | free_count += 1; |
| 92 | match self.data.get(next) { |
| 93 | Some(n) => next = *n, |
| 94 | None => internal_error("slot out of bounds" ), |
| 95 | }; |
| 96 | } |
| 97 | self.data.len() as u32 - free_count |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | fn internal_error(msg: &str) -> ! { |
| 102 | cfg_if::cfg_if! { |
| 103 | if #[cfg(debug_assertions)] { |
| 104 | super::throw_str(msg) |
| 105 | } else if #[cfg(feature = "std" )] { |
| 106 | std::process::abort(); |
| 107 | } else if #[cfg(all( |
| 108 | target_arch = "wasm32" , |
| 109 | any(target_os = "unknown" , target_os = "none" ) |
| 110 | ))] { |
| 111 | core::arch::wasm32::unreachable(); |
| 112 | } else { |
| 113 | unreachable!() |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | // Management of `externref` is always thread local since an `externref` value |
| 119 | // can't cross threads in wasm. Indices as a result are always thread-local. |
| 120 | #[cfg_attr (target_feature = "atomics" , thread_local)] |
| 121 | static HEAP_SLAB: crate::__rt::LazyCell<Cell<Slab>> = |
| 122 | crate::__rt::LazyCell::new(|| Cell::new(Slab::new())); |
| 123 | |
| 124 | #[no_mangle ] |
| 125 | pub extern "C" fn __externref_table_alloc() -> usize { |
| 126 | HEAP_SLAB |
| 127 | .try_with(|slot| { |
| 128 | let mut slab = slot.replace(Slab::new()); |
| 129 | let ret = slab.alloc(); |
| 130 | slot.replace(slab); |
| 131 | ret |
| 132 | }) |
| 133 | .unwrap_or_else(|_| internal_error(msg:"tls access failure" )) |
| 134 | } |
| 135 | |
| 136 | #[no_mangle ] |
| 137 | pub extern "C" fn __externref_table_dealloc(idx: usize) { |
| 138 | if idx < super::JSIDX_RESERVED as usize { |
| 139 | return; |
| 140 | } |
| 141 | // clear this value from the table so while the table slot is un-allocated |
| 142 | // we don't keep around a strong reference to a potentially large object |
| 143 | unsafe { |
| 144 | __wbindgen_externref_table_set_null(idx); |
| 145 | } |
| 146 | HEAP_SLAB |
| 147 | .try_with(|slot| { |
| 148 | let mut slab = slot.replace(Slab::new()); |
| 149 | slab.dealloc(idx); |
| 150 | slot.replace(slab); |
| 151 | }) |
| 152 | .unwrap_or_else(|_| internal_error(msg:"tls access failure" )) |
| 153 | } |
| 154 | |
| 155 | #[no_mangle ] |
| 156 | pub unsafe extern "C" fn __externref_drop_slice(ptr: *mut JsValue, len: usize) { |
| 157 | for slot: &mut JsValue in slice::from_raw_parts_mut(data:ptr, len) { |
| 158 | __externref_table_dealloc(slot.idx as usize); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | // Implementation of `__wbindgen_externref_heap_live_count` for when we are using |
| 163 | // `externref` instead of the JS `heap`. |
| 164 | #[no_mangle ] |
| 165 | pub unsafe extern "C" fn __externref_heap_live_count() -> u32 { |
| 166 | HEAP_SLAB |
| 167 | .try_with(|slot| { |
| 168 | let slab = slot.replace(Slab::new()); |
| 169 | let count = slab.live_count(); |
| 170 | slot.replace(slab); |
| 171 | count |
| 172 | }) |
| 173 | .unwrap_or_else(|_| internal_error(msg:"tls access failure" )) |
| 174 | } |
| 175 | |