1use crate::JsValue;
2use std::cell::Cell;
3use std::slice;
4use std::vec::Vec;
5use std::cmp::max;
6
7externs! {
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
15pub struct Slab {
16 data: Vec<usize>,
17 head: usize,
18 base: usize,
19}
20
21impl 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
100fn 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.
110std::thread_local!(pub static HEAP_SLAB: Cell<Slab> = Cell::new(Slab::new()));
111
112#[no_mangle]
113pub 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]
125pub 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]
144pub 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]
153pub 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)]
166pub fn link_intrinsics() {}
167