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