wasm_bindgen/
externref.rs
1use crate::JsValue;
2
3use alloc::slice;
4use alloc::vec::Vec;
5use core::cell::Cell;
6use core::cmp::max;
7
8externs! {
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
16pub struct Slab {
17 data: Vec<usize>,
18 head: usize,
19 base: usize,
20}
21
22impl 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 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 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 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
101fn 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#[cfg_attr(target_feature = "atomics", thread_local)]
121static HEAP_SLAB: crate::__rt::LazyCell<Cell<Slab>> =
122 crate::__rt::LazyCell::new(|| Cell::new(Slab::new()));
123
124#[no_mangle]
125pub 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("tls access failure"))
134}
135
136#[no_mangle]
137pub extern "C" fn __externref_table_dealloc(idx: usize) {
138 if idx < super::JSIDX_RESERVED as usize {
139 return;
140 }
141 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("tls access failure"))
153}
154
155#[no_mangle]
156pub unsafe extern "C" fn __externref_drop_slice(ptr: *mut JsValue, len: usize) {
157 for slot in slice::from_raw_parts_mut(ptr, len) {
158 __externref_table_dealloc(slot.idx as usize);
159 }
160}
161
162#[no_mangle]
165pub 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("tls access failure"))
174}