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