1use crate::prelude::*;
6use crate::runtime::vm::stack_switching::VMContObj;
7use crate::runtime::vm::vmcontext::{VMFuncRef, VMTableDefinition};
8use crate::runtime::vm::{GcStore, SendSyncPtr, VMGcRef, VMStore, VmPtr};
9use core::alloc::Layout;
10use core::mem;
11use core::ops::Range;
12use core::ptr::{self, NonNull};
13use core::slice;
14use core::{cmp, usize};
15use wasmtime_environ::{
16 FUNCREF_INIT_BIT, FUNCREF_MASK, IndexType, Trap, Tunables, WasmHeapTopType, WasmRefType,
17};
18
19pub enum TableElement {
24 FuncRef(Option<NonNull<VMFuncRef>>),
26
27 GcRef(Option<VMGcRef>),
29
30 UninitFunc,
35
36 ContRef(Option<VMContObj>),
38}
39
40#[derive(Copy, Clone, PartialEq, Eq, Debug)]
41pub enum TableElementType {
42 Func,
43 GcRef,
44 Cont,
45}
46
47impl TableElementType {
48 fn matches(&self, val: &TableElement) -> bool {
49 match (val, self) {
50 (TableElement::FuncRef(_), TableElementType::Func) => true,
51 (TableElement::GcRef(_), TableElementType::GcRef) => true,
52 (TableElement::ContRef(_), TableElementType::Cont) => true,
53 _ => false,
54 }
55 }
56
57 pub fn element_size(&self) -> usize {
59 match self {
60 TableElementType::Func => core::mem::size_of::<FuncTableElem>(),
61 TableElementType::GcRef => core::mem::size_of::<Option<VMGcRef>>(),
62 TableElementType::Cont => core::mem::size_of::<ContTableElem>(),
63 }
64 }
65}
66
67unsafe impl Send for TableElement where VMGcRef: Send {}
70unsafe impl Sync for TableElement where VMGcRef: Sync {}
71
72impl TableElement {
73 pub(crate) unsafe fn into_func_ref_asserting_initialized(self) -> Option<NonNull<VMFuncRef>> {
85 match self {
86 Self::FuncRef(e) => e,
87 Self::UninitFunc => panic!("Uninitialized table element value outside of table slot"),
88 Self::GcRef(_) => panic!("GC reference is not a function reference"),
89 Self::ContRef(_) => panic!("Continuation reference is not a function reference"),
90 }
91 }
92
93 pub(crate) fn is_uninit(&self) -> bool {
96 match self {
97 Self::UninitFunc => true,
98 _ => false,
99 }
100 }
101}
102
103impl From<Option<NonNull<VMFuncRef>>> for TableElement {
104 fn from(f: Option<NonNull<VMFuncRef>>) -> TableElement {
105 TableElement::FuncRef(f)
106 }
107}
108
109impl From<Option<VMGcRef>> for TableElement {
110 fn from(x: Option<VMGcRef>) -> TableElement {
111 TableElement::GcRef(x)
112 }
113}
114
115impl From<VMGcRef> for TableElement {
116 fn from(x: VMGcRef) -> TableElement {
117 TableElement::GcRef(Some(x))
118 }
119}
120
121impl From<Option<VMContObj>> for TableElement {
122 fn from(c: Option<VMContObj>) -> TableElement {
123 TableElement::ContRef(c)
124 }
125}
126
127impl From<VMContObj> for TableElement {
128 fn from(c: VMContObj) -> TableElement {
129 TableElement::ContRef(Some(c))
130 }
131}
132
133#[derive(Copy, Clone)]
147#[repr(transparent)]
148struct MaybeTaggedFuncRef(Option<VmPtr<VMFuncRef>>);
149
150impl MaybeTaggedFuncRef {
151 const UNINIT: MaybeTaggedFuncRef = MaybeTaggedFuncRef(None);
152
153 fn from(ptr: Option<NonNull<VMFuncRef>>, lazy_init: bool) -> Self {
156 let maybe_tagged = if lazy_init {
157 Some(match ptr {
158 Some(ptr) => ptr.map_addr(|a| a | FUNCREF_INIT_BIT),
159 None => NonNull::new(core::ptr::without_provenance_mut(FUNCREF_INIT_BIT)).unwrap(),
160 })
161 } else {
162 ptr
163 };
164 MaybeTaggedFuncRef(maybe_tagged.map(Into::into))
165 }
166
167 fn into_table_element(self, lazy_init: bool) -> TableElement {
170 let ptr = self.0;
171 if lazy_init && ptr.is_none() {
172 TableElement::UninitFunc
173 } else {
174 let unmasked =
177 ptr.and_then(|ptr| NonNull::new(ptr.as_ptr().map_addr(|a| a & FUNCREF_MASK)));
178 TableElement::FuncRef(unmasked)
179 }
180 }
181}
182
183pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>;
184pub type ContTableElem = Option<VMContObj>;
185
186#[cfg(feature = "pooling-allocator")]
188pub const NOMINAL_MAX_TABLE_ELEM_SIZE: usize = {
189 let sizes = [
191 core::mem::size_of::<FuncTableElem>(),
192 core::mem::size_of::<Option<VMGcRef>>(),
193 ];
194
195 const fn slice_max(data: &[usize]) -> usize {
198 match data {
199 [] => 0,
200 [head, tail @ ..] => {
201 let tail_max = slice_max(tail);
202 if *head >= tail_max { *head } else { tail_max }
203 }
204 }
205 }
206
207 slice_max(&sizes)
208};
209
210pub enum StaticTable {
211 Func(StaticFuncTable),
212 GcRef(StaticGcRefTable),
213 Cont(StaticContTable),
214}
215
216impl From<StaticFuncTable> for StaticTable {
217 fn from(value: StaticFuncTable) -> Self {
218 Self::Func(value)
219 }
220}
221
222impl From<StaticGcRefTable> for StaticTable {
223 fn from(value: StaticGcRefTable) -> Self {
224 Self::GcRef(value)
225 }
226}
227
228impl From<StaticContTable> for StaticTable {
229 fn from(value: StaticContTable) -> Self {
230 Self::Cont(value)
231 }
232}
233
234pub struct StaticFuncTable {
235 data: SendSyncPtr<[FuncTableElem]>,
238 size: usize,
240 lazy_init: bool,
242}
243
244pub struct StaticGcRefTable {
245 data: SendSyncPtr<[Option<VMGcRef>]>,
248 size: usize,
250}
251
252pub struct StaticContTable {
253 data: SendSyncPtr<[ContTableElem]>,
256 size: usize,
258}
259
260pub enum DynamicTable {
261 Func(DynamicFuncTable),
262 GcRef(DynamicGcRefTable),
263 Cont(DynamicContTable),
264}
265
266impl From<DynamicFuncTable> for DynamicTable {
267 fn from(value: DynamicFuncTable) -> Self {
268 Self::Func(value)
269 }
270}
271
272impl From<DynamicGcRefTable> for DynamicTable {
273 fn from(value: DynamicGcRefTable) -> Self {
274 Self::GcRef(value)
275 }
276}
277
278impl From<DynamicContTable> for DynamicTable {
279 fn from(value: DynamicContTable) -> Self {
280 Self::Cont(value)
281 }
282}
283
284pub struct DynamicFuncTable {
285 elements: Vec<FuncTableElem>,
288 maximum: Option<usize>,
290 lazy_init: bool,
292}
293
294pub struct DynamicGcRefTable {
295 elements: Vec<Option<VMGcRef>>,
298 maximum: Option<usize>,
300}
301
302pub struct DynamicContTable {
303 elements: Vec<ContTableElem>,
306 maximum: Option<usize>,
308}
309
310pub enum Table {
312 Static(StaticTable),
315 Dynamic(DynamicTable),
318}
319
320impl From<StaticTable> for Table {
321 fn from(value: StaticTable) -> Self {
322 Self::Static(value)
323 }
324}
325
326impl From<StaticFuncTable> for Table {
327 fn from(value: StaticFuncTable) -> Self {
328 let t: StaticTable = value.into();
329 t.into()
330 }
331}
332
333impl From<StaticGcRefTable> for Table {
334 fn from(value: StaticGcRefTable) -> Self {
335 let t: StaticTable = value.into();
336 t.into()
337 }
338}
339
340impl From<StaticContTable> for Table {
341 fn from(value: StaticContTable) -> Self {
342 let t: StaticTable = value.into();
343 t.into()
344 }
345}
346
347impl From<DynamicTable> for Table {
348 fn from(value: DynamicTable) -> Self {
349 Self::Dynamic(value)
350 }
351}
352
353impl From<DynamicFuncTable> for Table {
354 fn from(value: DynamicFuncTable) -> Self {
355 let t: DynamicTable = value.into();
356 t.into()
357 }
358}
359
360impl From<DynamicGcRefTable> for Table {
361 fn from(value: DynamicGcRefTable) -> Self {
362 let t: DynamicTable = value.into();
363 t.into()
364 }
365}
366
367impl From<DynamicContTable> for Table {
368 fn from(value: DynamicContTable) -> Self {
369 let t: DynamicTable = value.into();
370 t.into()
371 }
372}
373
374pub(crate) fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
375 match ty.heap_type.top() {
376 WasmHeapTopType::Func => TableElementType::Func,
377 WasmHeapTopType::Any | WasmHeapTopType::Extern => TableElementType::GcRef,
378 WasmHeapTopType::Cont => TableElementType::Cont,
379 }
380}
381
382unsafe fn alloc_dynamic_table_elements<T>(len: usize) -> Result<Vec<Option<T>>> {
393 debug_assert!(
394 core::mem::MaybeUninit::<Option<T>>::zeroed()
395 .assume_init()
396 .is_none(),
397 "null table elements are represented with zeroed memory"
398 );
399
400 if len == 0 {
401 return Ok(vec![]);
402 }
403
404 let align = mem::align_of::<Option<T>>();
405
406 let size = mem::size_of::<Option<T>>();
407 let size = size.next_multiple_of(align);
408 let size = size.checked_mul(len).unwrap();
409
410 let layout = Layout::from_size_align(size, align)?;
411
412 let ptr = alloc::alloc::alloc_zeroed(layout);
413 ensure!(!ptr.is_null(), "failed to allocate memory for table");
414
415 let elems = Vec::<Option<T>>::from_raw_parts(ptr.cast(), len, len);
416 debug_assert!(elems.iter().all(|e| e.is_none()));
417
418 Ok(elems)
419}
420
421impl Table {
422 pub fn new_dynamic(
424 ty: &wasmtime_environ::Table,
425 tunables: &Tunables,
426 store: &mut dyn VMStore,
427 ) -> Result<Self> {
428 let (minimum, maximum) = Self::limit_new(ty, store)?;
429 match wasm_to_table_type(ty.ref_type) {
430 TableElementType::Func => Ok(Self::from(DynamicFuncTable {
431 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
432 maximum,
433 lazy_init: tunables.table_lazy_init,
434 })),
435 TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable {
436 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
437 maximum,
438 })),
439 TableElementType::Cont => Ok(Self::from(DynamicContTable {
440 elements: vec![None; minimum],
441 maximum,
442 })),
443 }
444 }
445
446 pub unsafe fn new_static(
448 ty: &wasmtime_environ::Table,
449 tunables: &Tunables,
450 data: SendSyncPtr<[u8]>,
451 store: &mut dyn VMStore,
452 ) -> Result<Self> {
453 let (minimum, maximum) = Self::limit_new(ty, store)?;
454 let size = minimum;
455 let max = maximum.unwrap_or(usize::MAX);
456
457 match wasm_to_table_type(ty.ref_type) {
458 TableElementType::Func => {
459 let len = {
460 let data = data.as_non_null().as_ref();
461 let (before, data, after) = data.align_to::<FuncTableElem>();
462 assert!(before.is_empty());
463 assert!(after.is_empty());
464 data.len()
465 };
466 ensure!(
467 usize::try_from(ty.limits.min).unwrap() <= len,
468 "initial table size of {} exceeds the pooling allocator's \
469 configured maximum table size of {len} elements",
470 ty.limits.min,
471 );
472 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
473 data.as_non_null().cast::<FuncTableElem>(),
474 cmp::min(len, max),
475 ));
476 Ok(Self::from(StaticFuncTable {
477 data,
478 size,
479 lazy_init: tunables.table_lazy_init,
480 }))
481 }
482 TableElementType::GcRef => {
483 let len = {
484 let data = data.as_non_null().as_ref();
485 let (before, data, after) = data.align_to::<Option<VMGcRef>>();
486 assert!(before.is_empty());
487 assert!(after.is_empty());
488 data.len()
489 };
490 ensure!(
491 usize::try_from(ty.limits.min).unwrap() <= len,
492 "initial table size of {} exceeds the pooling allocator's \
493 configured maximum table size of {len} elements",
494 ty.limits.min,
495 );
496 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
497 data.as_non_null().cast::<Option<VMGcRef>>(),
498 cmp::min(len, max),
499 ));
500 Ok(Self::from(StaticGcRefTable { data, size }))
501 }
502 TableElementType::Cont => {
503 let len = {
504 let data = data.as_non_null().as_ref();
505 let (before, data, after) = data.align_to::<ContTableElem>();
506 assert!(before.is_empty());
507 assert!(after.is_empty());
508 data.len()
509 };
510 ensure!(
511 usize::try_from(ty.limits.min).unwrap() <= len,
512 "initial table size of {} exceeds the pooling allocator's \
513 configured maximum table size of {len} elements",
514 ty.limits.min,
515 );
516 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
517 data.as_non_null().cast::<ContTableElem>(),
518 cmp::min(len, max),
519 ));
520 Ok(Self::from(StaticContTable { data, size }))
521 }
522 }
523 }
524
525 fn limit_new(
529 ty: &wasmtime_environ::Table,
530 store: &mut dyn VMStore,
531 ) -> Result<(usize, Option<usize>)> {
532 let absolute_max = usize::MAX;
535
536 let minimum = usize::try_from(ty.limits.min).ok();
539
540 let maximum = match (ty.limits.max, ty.idx_type) {
545 (Some(max), _) => usize::try_from(max).ok(),
546 (None, IndexType::I64) => usize::try_from(u64::MAX).ok(),
547 (None, IndexType::I32) => usize::try_from(u32::MAX).ok(),
548 };
549
550 if !store.table_growing(0, minimum.unwrap_or(absolute_max), maximum)? {
552 bail!(
553 "table minimum size of {} elements exceeds table limits",
554 ty.limits.min
555 );
556 }
557
558 let minimum = minimum.ok_or_else(|| {
561 format_err!(
562 "table minimum size of {} elements exceeds table limits",
563 ty.limits.min
564 )
565 })?;
566 Ok((minimum, maximum))
567 }
568
569 pub fn element_type(&self) -> TableElementType {
571 match self {
572 Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => {
573 TableElementType::Func
574 }
575 Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => {
576 TableElementType::GcRef
577 }
578 Table::Static(StaticTable::Cont(_)) | Table::Dynamic(DynamicTable::Cont(_)) => {
579 TableElementType::Cont
580 }
581 }
582 }
583
584 #[cfg(feature = "pooling-allocator")]
586 pub(crate) fn is_static(&self) -> bool {
587 matches!(self, Table::Static(_))
588 }
589
590 pub fn size(&self) -> usize {
592 match self {
593 Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size,
594 Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size,
595 Table::Static(StaticTable::Cont(StaticContTable { size, .. })) => *size,
596 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => elements.len(),
597 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
598 elements.len()
599 }
600 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => elements.len(),
601 }
602 }
603
604 pub fn maximum(&self) -> Option<usize> {
611 match self {
612 Table::Static(StaticTable::Cont(StaticContTable { data, .. })) => Some(data.len()),
613 Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => Some(data.len()),
614 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => Some(data.len()),
615 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum,
616 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum,
617 Table::Dynamic(DynamicTable::Cont(DynamicContTable { maximum, .. })) => *maximum,
618 }
619 }
620
621 pub fn init_func(
627 &mut self,
628 dst: u64,
629 items: impl ExactSizeIterator<Item = Option<NonNull<VMFuncRef>>>,
630 ) -> Result<(), Trap> {
631 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
632
633 let (funcrefs, lazy_init) = self.funcrefs_mut();
634 let elements = funcrefs
635 .get_mut(dst..)
636 .and_then(|s| s.get_mut(..items.len()))
637 .ok_or(Trap::TableOutOfBounds)?;
638
639 for (item, slot) in items.zip(elements) {
640 *slot = MaybeTaggedFuncRef::from(item, lazy_init);
641 }
642 Ok(())
643 }
644
645 pub fn init_gc_refs(
649 &mut self,
650 dst: u64,
651 items: impl ExactSizeIterator<Item = Option<VMGcRef>>,
652 ) -> Result<(), Trap> {
653 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
654
655 let elements = self
656 .gc_refs_mut()
657 .get_mut(dst..)
658 .and_then(|s| s.get_mut(..items.len()))
659 .ok_or(Trap::TableOutOfBounds)?;
660
661 for (item, slot) in items.zip(elements) {
662 *slot = item;
663 }
664 Ok(())
665 }
666
667 pub fn fill(
676 &mut self,
677 mut gc_store: Option<&mut GcStore>,
678 dst: u64,
679 val: TableElement,
680 len: u64,
681 ) -> Result<(), Trap> {
682 let start = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
683 let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
684 let end = start
685 .checked_add(len)
686 .ok_or_else(|| Trap::TableOutOfBounds)?;
687
688 if end > self.size() {
689 return Err(Trap::TableOutOfBounds);
690 }
691
692 match val {
693 TableElement::FuncRef(f) => {
694 let (funcrefs, lazy_init) = self.funcrefs_mut();
695 funcrefs[start..end].fill(MaybeTaggedFuncRef::from(f, lazy_init));
696 }
697 TableElement::GcRef(r) => {
698 for slot in &mut self.gc_refs_mut()[start..end] {
700 match gc_store.as_deref_mut() {
701 Some(s) => s.write_gc_ref(slot, r.as_ref()),
702 None => {
703 debug_assert!(slot.as_ref().is_none_or(|x| x.is_i31()));
704 debug_assert!(r.as_ref().is_none_or(|r| r.is_i31()));
705 *slot = r.as_ref().map(|r| r.copy_i31());
706 }
707 }
708 }
709
710 if let Some(r) = r {
713 match gc_store {
714 Some(s) => s.drop_gc_ref(r),
715 None => debug_assert!(r.is_i31()),
716 }
717 }
718 }
719 TableElement::UninitFunc => {
720 let (funcrefs, _lazy_init) = self.funcrefs_mut();
721 funcrefs[start..end].fill(MaybeTaggedFuncRef::UNINIT);
722 }
723 TableElement::ContRef(c) => {
724 let contrefs = self.contrefs_mut();
725 contrefs[start..end].fill(c);
726 }
727 }
728
729 Ok(())
730 }
731
732 pub unsafe fn grow(
753 &mut self,
754 delta: u64,
755 init_value: TableElement,
756 store: &mut dyn VMStore,
757 ) -> Result<Option<usize>, Error> {
758 let old_size = self.size();
759
760 if delta == 0 {
763 return Ok(Some(old_size));
764 }
765 let delta = usize::try_from(delta).map_err(|_| Trap::TableOutOfBounds)?;
766
767 let new_size = match old_size.checked_add(delta) {
768 Some(s) => s,
769 None => {
770 store.table_grow_failed(format_err!("overflow calculating new table size"))?;
771 return Ok(None);
772 }
773 };
774
775 if !store.table_growing(old_size, new_size, self.maximum())? {
776 return Ok(None);
777 }
778
779 if let Some(max) = self.maximum() {
783 if new_size > max {
784 store.table_grow_failed(format_err!("Table maximum size exceeded"))?;
785 return Ok(None);
786 }
787 }
788
789 debug_assert!(self.type_matches(&init_value));
790
791 match self {
793 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
794 unsafe {
795 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
796 }
797 *size = new_size;
798 }
799 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
800 unsafe {
801 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
802 }
803 *size = new_size;
804 }
805 Table::Static(StaticTable::Cont(StaticContTable { data, size })) => {
806 unsafe {
807 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
808 }
809 *size = new_size;
810 }
811
812 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
820 elements.resize(new_size, None);
821 }
822 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
823 elements.resize_with(new_size, || None);
824 }
825 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
826 elements.resize(new_size, None);
827 }
828 }
829
830 self.fill(
831 store.store_opaque_mut().optional_gc_store_mut(),
832 u64::try_from(old_size).unwrap(),
833 init_value,
834 u64::try_from(delta).unwrap(),
835 )
836 .expect("table should not be out of bounds");
837
838 Ok(Some(old_size))
839 }
840
841 pub fn get(&self, gc_store: Option<&mut GcStore>, index: u64) -> Option<TableElement> {
847 let index = usize::try_from(index).ok()?;
848 match self.element_type() {
849 TableElementType::Func => {
850 let (funcrefs, lazy_init) = self.funcrefs();
851 funcrefs
852 .get(index)
853 .copied()
854 .map(|e| e.into_table_element(lazy_init))
855 }
856 TableElementType::GcRef => self.gc_refs().get(index).map(|r| {
857 let r = r.as_ref().map(|r| match gc_store {
858 Some(s) => s.clone_gc_ref(r),
859 None => r.copy_i31(),
860 });
861
862 TableElement::GcRef(r)
863 }),
864 TableElementType::Cont => self
865 .contrefs()
866 .get(index)
867 .copied()
868 .map(|e| TableElement::ContRef(e)),
869 }
870 }
871
872 pub fn set(&mut self, index: u64, elem: TableElement) -> Result<(), ()> {
883 let index: usize = index.try_into().map_err(|_| ())?;
884 match elem {
885 TableElement::FuncRef(f) => {
886 let (funcrefs, lazy_init) = self.funcrefs_mut();
887 *funcrefs.get_mut(index).ok_or(())? = MaybeTaggedFuncRef::from(f, lazy_init);
888 }
889 TableElement::UninitFunc => {
890 let (funcrefs, _lazy_init) = self.funcrefs_mut();
891 *funcrefs.get_mut(index).ok_or(())? = MaybeTaggedFuncRef::UNINIT;
892 }
893 TableElement::GcRef(e) => {
894 *self.gc_refs_mut().get_mut(index).ok_or(())? = e;
895 }
896 TableElement::ContRef(c) => {
897 *self.contrefs_mut().get_mut(index).ok_or(())? = c;
898 }
899 }
900 Ok(())
901 }
902
903 pub unsafe fn copy(
910 gc_store: Option<&mut GcStore>,
911 dst_table: *mut Self,
912 src_table: *mut Self,
913 dst_index: u64,
914 src_index: u64,
915 len: u64,
916 ) -> Result<(), Trap> {
917 let src_index = usize::try_from(src_index).map_err(|_| Trap::TableOutOfBounds)?;
920 let dst_index = usize::try_from(dst_index).map_err(|_| Trap::TableOutOfBounds)?;
921 let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
922
923 if src_index
924 .checked_add(len)
925 .map_or(true, |n| n > (*src_table).size())
926 || dst_index
927 .checked_add(len)
928 .map_or(true, |m| m > (*dst_table).size())
929 {
930 return Err(Trap::TableOutOfBounds);
931 }
932
933 debug_assert!(
934 (*dst_table).element_type() == (*src_table).element_type(),
935 "table element type mismatch"
936 );
937
938 let src_range = src_index..src_index + len;
939 let dst_range = dst_index..dst_index + len;
940
941 if ptr::eq(dst_table, src_table) {
943 (*dst_table).copy_elements_within(gc_store, dst_range, src_range);
944 } else {
945 Self::copy_elements(gc_store, &mut *dst_table, &*src_table, dst_range, src_range);
946 }
947
948 Ok(())
949 }
950
951 pub fn vmtable(&mut self) -> VMTableDefinition {
953 match self {
954 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
955 VMTableDefinition {
956 base: data.cast().into(),
957 current_elements: *size,
958 }
959 }
960 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
961 VMTableDefinition {
962 base: data.cast().into(),
963 current_elements: *size,
964 }
965 }
966 Table::Static(StaticTable::Cont(StaticContTable { data, size })) => VMTableDefinition {
967 base: data.cast().into(),
968 current_elements: *size,
969 },
970 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
971 VMTableDefinition {
972 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
973 current_elements: elements.len(),
974 }
975 }
976 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
977 VMTableDefinition {
978 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
979 current_elements: elements.len(),
980 }
981 }
982 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
983 VMTableDefinition {
984 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
985 current_elements: elements.len(),
986 }
987 }
988 }
989 }
990
991 fn type_matches(&self, val: &TableElement) -> bool {
992 self.element_type().matches(val)
993 }
994
995 fn funcrefs(&self) -> (&[MaybeTaggedFuncRef], bool) {
996 assert_eq!(self.element_type(), TableElementType::Func);
997 match self {
998 Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
999 elements,
1000 lazy_init,
1001 ..
1002 })) => (
1003 unsafe { slice::from_raw_parts(elements.as_ptr().cast(), elements.len()) },
1004 *lazy_init,
1005 ),
1006 Self::Static(StaticTable::Func(StaticFuncTable {
1007 data,
1008 size,
1009 lazy_init,
1010 })) => (
1011 unsafe { slice::from_raw_parts(data.as_ptr().cast(), *size) },
1012 *lazy_init,
1013 ),
1014 _ => unreachable!(),
1015 }
1016 }
1017
1018 fn funcrefs_mut(&mut self) -> (&mut [MaybeTaggedFuncRef], bool) {
1019 assert_eq!(self.element_type(), TableElementType::Func);
1020 match self {
1021 Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
1022 elements,
1023 lazy_init,
1024 ..
1025 })) => (
1026 unsafe { slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len()) },
1027 *lazy_init,
1028 ),
1029 Self::Static(StaticTable::Func(StaticFuncTable {
1030 data,
1031 size,
1032 lazy_init,
1033 })) => (
1034 unsafe { slice::from_raw_parts_mut(data.as_ptr().cast(), *size) },
1035 *lazy_init,
1036 ),
1037 _ => unreachable!(),
1038 }
1039 }
1040
1041 fn gc_refs(&self) -> &[Option<VMGcRef>] {
1042 assert_eq!(self.element_type(), TableElementType::GcRef);
1043 match self {
1044 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
1045 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
1046 &data.as_non_null().as_ref()[..*size]
1047 },
1048 _ => unreachable!(),
1049 }
1050 }
1051
1052 fn contrefs(&self) -> &[Option<VMContObj>] {
1053 assert_eq!(self.element_type(), TableElementType::Cont);
1054 match self {
1055 Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1056 slice::from_raw_parts(elements.as_ptr().cast(), elements.len())
1057 },
1058 Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1059 slice::from_raw_parts(data.as_ptr().cast(), *size)
1060 },
1061 _ => unreachable!(),
1062 }
1063 }
1064
1065 fn contrefs_mut(&mut self) -> &mut [Option<VMContObj>] {
1066 assert_eq!(self.element_type(), TableElementType::Cont);
1067 match self {
1068 Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1069 slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len())
1070 },
1071 Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1072 slice::from_raw_parts_mut(data.as_ptr().cast(), *size)
1073 },
1074 _ => unreachable!(),
1075 }
1076 }
1077
1078 pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] {
1082 assert_eq!(self.element_type(), TableElementType::GcRef);
1083 match self {
1084 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
1085 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
1086 &mut data.as_non_null().as_mut()[..*size]
1087 },
1088 _ => unreachable!(),
1089 }
1090 }
1091
1092 fn copy_elements(
1093 gc_store: Option<&mut GcStore>,
1094 dst_table: &mut Self,
1095 src_table: &Self,
1096 dst_range: Range<usize>,
1097 src_range: Range<usize>,
1098 ) {
1099 debug_assert!(!ptr::eq(dst_table, src_table));
1101
1102 let ty = dst_table.element_type();
1103
1104 match ty {
1105 TableElementType::Func => {
1106 let (dst_funcrefs, _lazy_init) = dst_table.funcrefs_mut();
1108 let (src_funcrefs, _lazy_init) = src_table.funcrefs();
1109 dst_funcrefs[dst_range].copy_from_slice(&src_funcrefs[src_range]);
1110 }
1111 TableElementType::GcRef => {
1112 assert_eq!(
1113 dst_range.end - dst_range.start,
1114 src_range.end - src_range.start
1115 );
1116 assert!(dst_range.end <= dst_table.gc_refs().len());
1117 assert!(src_range.end <= src_table.gc_refs().len());
1118 let gc_store = gc_store.unwrap();
1119 for (dst, src) in dst_range.zip(src_range) {
1120 gc_store.write_gc_ref(
1121 &mut dst_table.gc_refs_mut()[dst],
1122 src_table.gc_refs()[src].as_ref(),
1123 );
1124 }
1125 }
1126 TableElementType::Cont => {
1127 dst_table.contrefs_mut()[dst_range]
1129 .copy_from_slice(&src_table.contrefs()[src_range]);
1130 }
1131 }
1132 }
1133
1134 fn copy_elements_within(
1135 &mut self,
1136 gc_store: Option<&mut GcStore>,
1137 dst_range: Range<usize>,
1138 src_range: Range<usize>,
1139 ) {
1140 assert_eq!(
1141 dst_range.end - dst_range.start,
1142 src_range.end - src_range.start
1143 );
1144
1145 if src_range.start == dst_range.start {
1147 return;
1148 }
1149
1150 let ty = self.element_type();
1151 match ty {
1152 TableElementType::Func => {
1153 let (funcrefs, _lazy_init) = self.funcrefs_mut();
1155 funcrefs.copy_within(src_range, dst_range.start);
1156 }
1157 TableElementType::GcRef => {
1158 let gc_store = gc_store.unwrap();
1159
1160 let elements = self.gc_refs_mut();
1163 if dst_range.start < src_range.start {
1164 for (d, s) in dst_range.zip(src_range) {
1165 let (ds, ss) = elements.split_at_mut(s);
1166 let dst = &mut ds[d];
1167 let src = ss[0].as_ref();
1168 gc_store.write_gc_ref(dst, src);
1169 }
1170 } else {
1171 for (s, d) in src_range.rev().zip(dst_range.rev()) {
1172 let (ss, ds) = elements.split_at_mut(d);
1173 let dst = &mut ds[0];
1174 let src = ss[s].as_ref();
1175 gc_store.write_gc_ref(dst, src);
1176 }
1177 }
1178 }
1179 TableElementType::Cont => {
1180 self.contrefs_mut().copy_within(src_range, dst_range.start);
1182 }
1183 }
1184 }
1185}
1186
1187impl Default for Table {
1189 fn default() -> Self {
1190 Self::from(StaticFuncTable {
1191 data: SendSyncPtr::new(NonNull::from(&mut [])),
1192 size: 0,
1193 lazy_init: false,
1194 })
1195 }
1196}