1use crate::func_data_registry::VMFuncRef;
9use crate::trap::{Trap, TrapCode};
10use crate::vmcontext::VMTableDefinition;
11use crate::VMExternRef;
12use std::borrow::BorrowMut;
13use std::cell::UnsafeCell;
14use std::convert::TryFrom;
15use std::fmt;
16use std::ptr::NonNull;
17use std::sync::Mutex;
18use unc_vm_types::{ExternRef, TableType, Type as ValType};
19
20#[derive(Debug, Clone, Hash, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
22pub enum TableStyle {
23 CallerChecksSignature,
25}
26
27pub trait Table: fmt::Debug + Send + Sync {
29 fn style(&self) -> &TableStyle;
31
32 fn ty(&self) -> &TableType;
34
35 fn size(&self) -> u32;
37
38 fn grow(&self, delta: u32, init_value: TableElement) -> Option<u32>;
43
44 fn get(&self, index: u32) -> Option<TableElement>;
48
49 fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap>;
55
56 fn vmtable(&self) -> NonNull<VMTableDefinition>;
58
59 fn copy(
66 &self,
67 src_table: &dyn Table,
68 dst_index: u32,
69 src_index: u32,
70 len: u32,
71 ) -> Result<(), Trap> {
72 if src_index.checked_add(len).map_or(true, |n| n > src_table.size()) {
75 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
76 }
77
78 if dst_index.checked_add(len).map_or(true, |m| m > self.size()) {
79 return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
80 }
81
82 let srcs = src_index..src_index + len;
83 let dsts = dst_index..dst_index + len;
84
85 if dst_index <= src_index {
90 for (s, d) in (srcs).zip(dsts) {
91 self.set(d, src_table.get(s).unwrap())?;
92 }
93 } else {
94 for (s, d) in srcs.rev().zip(dsts.rev()) {
95 self.set(d, src_table.get(s).unwrap())?;
96 }
97 }
98
99 Ok(())
100 }
101}
102
103#[derive(Debug, Clone)]
105pub enum TableElement {
106 ExternRef(ExternRef),
110 FuncRef(VMFuncRef),
112}
113
114impl From<TableElement> for RawTableElement {
115 fn from(other: TableElement) -> Self {
116 match other {
117 TableElement::ExternRef(extern_ref) => Self { extern_ref: extern_ref.into() },
118 TableElement::FuncRef(func_ref) => Self { func_ref },
119 }
120 }
121}
122
123#[repr(C)]
124#[derive(Clone, Copy)]
125pub union RawTableElement {
126 pub(crate) extern_ref: VMExternRef,
127 pub(crate) func_ref: VMFuncRef,
128}
129
130#[cfg(test)]
131#[test]
132fn table_element_size_test() {
133 use std::mem::size_of;
134 assert_eq!(size_of::<RawTableElement>(), size_of::<VMExternRef>());
135 assert_eq!(size_of::<RawTableElement>(), size_of::<VMFuncRef>());
136}
137
138impl fmt::Debug for RawTableElement {
139 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140 f.debug_struct("RawTableElement").finish()
141 }
142}
143
144impl Default for RawTableElement {
145 fn default() -> Self {
146 Self { func_ref: VMFuncRef::null() }
147 }
148}
149
150impl Default for TableElement {
151 fn default() -> Self {
152 Self::FuncRef(VMFuncRef::null())
153 }
154}
155
156#[derive(Debug)]
158pub struct LinearTable {
159 vec: Mutex<Vec<RawTableElement>>,
161 maximum: Option<u32>,
162 table: TableType,
164 style: TableStyle,
166 vm_table_definition: VMTableDefinitionOwnership,
167}
168
169#[derive(Debug)]
172enum VMTableDefinitionOwnership {
173 VMOwned(NonNull<VMTableDefinition>),
176 HostOwned(Box<UnsafeCell<VMTableDefinition>>),
180}
181
182unsafe impl Send for LinearTable {}
184unsafe impl Sync for LinearTable {}
186
187impl LinearTable {
188 pub fn new(table: &TableType, style: &TableStyle) -> Result<Self, String> {
193 unsafe { Self::new_inner(table, style, None) }
194 }
195
196 pub unsafe fn from_definition(
204 table: &TableType,
205 style: &TableStyle,
206 vm_table_location: NonNull<VMTableDefinition>,
207 ) -> Result<Self, String> {
208 Self::new_inner(table, style, Some(vm_table_location))
209 }
210
211 unsafe fn new_inner(
213 table: &TableType,
214 style: &TableStyle,
215 vm_table_location: Option<NonNull<VMTableDefinition>>,
216 ) -> Result<Self, String> {
217 match table.ty {
218 ValType::FuncRef | ValType::ExternRef => (),
219 ty => return Err(format!("tables of types other than funcref or externref ({})", ty)),
220 };
221 if let Some(max) = table.maximum {
222 if max < table.minimum {
223 return Err(format!(
224 "Table minimum ({}) is larger than maximum ({})!",
225 table.minimum, max
226 ));
227 }
228 }
229 let table_minimum = usize::try_from(table.minimum)
230 .map_err(|_| "Table minimum is bigger than usize".to_string())?;
231 let mut vec = vec![RawTableElement::default(); table_minimum];
232 let base = vec.as_mut_ptr();
233 match style {
234 TableStyle::CallerChecksSignature => Ok(Self {
235 vec: Mutex::new(vec),
236 maximum: table.maximum,
237 table: *table,
238 style: style.clone(),
239 vm_table_definition: if let Some(table_loc) = vm_table_location {
240 {
241 let mut ptr = table_loc;
242 let td = ptr.as_mut();
243 td.base = base as _;
244 td.current_elements = table_minimum as _;
245 }
246 VMTableDefinitionOwnership::VMOwned(table_loc)
247 } else {
248 VMTableDefinitionOwnership::HostOwned(Box::new(UnsafeCell::new(
249 VMTableDefinition { base: base as _, current_elements: table_minimum as _ },
250 )))
251 },
252 }),
253 }
254 }
255
256 unsafe fn get_vm_table_definition(&self) -> NonNull<VMTableDefinition> {
262 match &self.vm_table_definition {
263 VMTableDefinitionOwnership::VMOwned(ptr) => *ptr,
264 VMTableDefinitionOwnership::HostOwned(boxed_ptr) => {
265 NonNull::new_unchecked(boxed_ptr.get())
266 }
267 }
268 }
269}
270
271impl Table for LinearTable {
272 fn ty(&self) -> &TableType {
274 &self.table
275 }
276
277 fn style(&self) -> &TableStyle {
279 &self.style
280 }
281
282 fn size(&self) -> u32 {
284 unsafe {
286 let td_ptr = self.get_vm_table_definition();
287 let td = td_ptr.as_ref();
288 td.current_elements
289 }
290 }
291
292 fn grow(&self, delta: u32, init_value: TableElement) -> Option<u32> {
297 let mut vec_guard = self.vec.lock().unwrap();
298 let vec = vec_guard.borrow_mut();
299 let size = self.size();
300 let new_len = size.checked_add(delta)?;
301 if self.maximum.map_or(false, |max| new_len > max) {
302 return None;
303 }
304 if new_len == size {
305 debug_assert_eq!(delta, 0);
306 return Some(size);
307 }
308
309 let element = match init_value {
311 TableElement::ExternRef(extern_ref) => {
312 let extern_ref: VMExternRef = extern_ref.into();
313 if let Some(val) = (new_len as usize).checked_sub(size as usize + 1) {
316 extern_ref.ref_inc_by(val);
317 }
318 RawTableElement { extern_ref }
319 }
320 TableElement::FuncRef(func_ref) => RawTableElement { func_ref },
321 };
322
323 vec.resize(usize::try_from(new_len).unwrap(), element);
324
325 unsafe {
327 let mut td_ptr = self.get_vm_table_definition();
328 let td = td_ptr.as_mut();
329 td.current_elements = new_len;
330 td.base = vec.as_mut_ptr() as _;
331 }
332 Some(size)
333 }
334
335 fn get(&self, index: u32) -> Option<TableElement> {
339 let vec_guard = self.vec.lock().unwrap();
340 let raw_data = vec_guard.get(index as usize).cloned()?;
341 Some(match self.table.ty {
342 ValType::ExternRef => {
343 TableElement::ExternRef(unsafe { raw_data.extern_ref.ref_clone() }.into())
344 }
345 ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }),
346 _ => todo!("getting invalid type from table, handle this error"),
347 })
348 }
349
350 fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap> {
356 let mut vec_guard = self.vec.lock().unwrap();
357 let vec = vec_guard.borrow_mut();
358 match vec.get_mut(index as usize) {
359 Some(slot) => {
360 match (self.table.ty, reference) {
361 (ValType::ExternRef, TableElement::ExternRef(extern_ref)) => {
362 let extern_ref = extern_ref.into();
363 unsafe {
364 let elem = &mut *slot;
365 elem.extern_ref.ref_drop();
366 elem.extern_ref = extern_ref
367 }
368 }
369 (ValType::FuncRef, r @ TableElement::FuncRef(_)) => {
370 let element_data = r.into();
371 *slot = element_data;
372 }
373 (ty, v) => {
376 panic!("Attempted to set a table of type {} with the value {:?}", ty, v)
377 }
378 };
379
380 Ok(())
381 }
382 None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)),
383 }
384 }
385
386 fn vmtable(&self) -> NonNull<VMTableDefinition> {
388 let _vec_guard = self.vec.lock().unwrap();
389 unsafe { self.get_vm_table_definition() }
390 }
391}