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 | |