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