1use crate::prelude::*;
6use crate::runtime::store::StoreResourceLimiter;
7use crate::runtime::vm::stack_switching::VMContObj;
8use crate::runtime::vm::vmcontext::{VMFuncRef, VMTableDefinition};
9use crate::runtime::vm::{GcStore, SendSyncPtr, VMGcRef, VmPtr};
10use core::alloc::Layout;
11use core::mem;
12use core::ops::Range;
13use core::ptr::{self, NonNull};
14use core::slice;
15use core::{cmp, usize};
16use wasmtime_environ::{
17 FUNCREF_INIT_BIT, FUNCREF_MASK, IndexType, Trap, Tunables, WasmHeapTopType, WasmRefType,
18};
19
20#[derive(Copy, Clone, PartialEq, Eq, Debug)]
21pub enum TableElementType {
22 Func,
23 GcRef,
24 Cont,
25}
26
27impl TableElementType {
28 pub fn element_size(&self) -> usize {
30 match self {
31 TableElementType::Func => core::mem::size_of::<FuncTableElem>(),
32 TableElementType::GcRef => core::mem::size_of::<Option<VMGcRef>>(),
33 TableElementType::Cont => core::mem::size_of::<ContTableElem>(),
34 }
35 }
36}
37
38#[derive(Copy, Clone)]
52#[repr(transparent)]
53struct MaybeTaggedFuncRef(Option<VmPtr<VMFuncRef>>);
54
55impl MaybeTaggedFuncRef {
56 fn from(ptr: Option<NonNull<VMFuncRef>>, lazy_init: bool) -> Self {
59 let maybe_tagged = if lazy_init {
60 Some(match ptr {
61 Some(ptr) => ptr.map_addr(|a| a | FUNCREF_INIT_BIT),
62 None => NonNull::new(core::ptr::without_provenance_mut(FUNCREF_INIT_BIT)).unwrap(),
63 })
64 } else {
65 ptr
66 };
67 MaybeTaggedFuncRef(maybe_tagged.map(Into::into))
68 }
69
70 fn into_funcref(self, lazy_init: bool) -> Option<Option<NonNull<VMFuncRef>>> {
73 let ptr = self.0;
74 if lazy_init && ptr.is_none() {
75 None
76 } else {
77 Some(ptr.and_then(|ptr| NonNull::new(ptr.as_ptr().map_addr(|a| a & FUNCREF_MASK))))
80 }
81 }
82}
83
84pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>;
85pub type ContTableElem = Option<VMContObj>;
86
87#[cfg(feature = "pooling-allocator")]
89pub const NOMINAL_MAX_TABLE_ELEM_SIZE: usize = {
90 let sizes = [
92 core::mem::size_of::<FuncTableElem>(),
93 core::mem::size_of::<Option<VMGcRef>>(),
94 ];
95
96 const fn slice_max(data: &[usize]) -> usize {
99 match data {
100 [] => 0,
101 [head, tail @ ..] => {
102 let tail_max = slice_max(tail);
103 if *head >= tail_max { *head } else { tail_max }
104 }
105 }
106 }
107
108 slice_max(&sizes)
109};
110
111pub enum StaticTable {
112 Func(StaticFuncTable),
113 GcRef(StaticGcRefTable),
114 Cont(StaticContTable),
115}
116
117impl From<StaticFuncTable> for StaticTable {
118 fn from(value: StaticFuncTable) -> Self {
119 Self::Func(value)
120 }
121}
122
123impl From<StaticGcRefTable> for StaticTable {
124 fn from(value: StaticGcRefTable) -> Self {
125 Self::GcRef(value)
126 }
127}
128
129impl From<StaticContTable> for StaticTable {
130 fn from(value: StaticContTable) -> Self {
131 Self::Cont(value)
132 }
133}
134
135pub struct StaticFuncTable {
136 data: SendSyncPtr<[FuncTableElem]>,
139 size: usize,
141 lazy_init: bool,
143}
144
145pub struct StaticGcRefTable {
146 data: SendSyncPtr<[Option<VMGcRef>]>,
149 size: usize,
151}
152
153pub struct StaticContTable {
154 data: SendSyncPtr<[ContTableElem]>,
157 size: usize,
159}
160
161pub enum DynamicTable {
162 Func(DynamicFuncTable),
163 GcRef(DynamicGcRefTable),
164 Cont(DynamicContTable),
165}
166
167impl From<DynamicFuncTable> for DynamicTable {
168 fn from(value: DynamicFuncTable) -> Self {
169 Self::Func(value)
170 }
171}
172
173impl From<DynamicGcRefTable> for DynamicTable {
174 fn from(value: DynamicGcRefTable) -> Self {
175 Self::GcRef(value)
176 }
177}
178
179impl From<DynamicContTable> for DynamicTable {
180 fn from(value: DynamicContTable) -> Self {
181 Self::Cont(value)
182 }
183}
184
185pub struct DynamicFuncTable {
186 elements: Vec<FuncTableElem>,
189 maximum: Option<usize>,
191 lazy_init: bool,
193}
194
195pub struct DynamicGcRefTable {
196 elements: Vec<Option<VMGcRef>>,
199 maximum: Option<usize>,
201}
202
203pub struct DynamicContTable {
204 elements: Vec<ContTableElem>,
207 maximum: Option<usize>,
209}
210
211pub enum Table {
213 Static(StaticTable),
216 Dynamic(DynamicTable),
219}
220
221impl From<StaticTable> for Table {
222 fn from(value: StaticTable) -> Self {
223 Self::Static(value)
224 }
225}
226
227impl From<StaticFuncTable> for Table {
228 fn from(value: StaticFuncTable) -> Self {
229 let t: StaticTable = value.into();
230 t.into()
231 }
232}
233
234impl From<StaticGcRefTable> for Table {
235 fn from(value: StaticGcRefTable) -> Self {
236 let t: StaticTable = value.into();
237 t.into()
238 }
239}
240
241impl From<StaticContTable> for Table {
242 fn from(value: StaticContTable) -> Self {
243 let t: StaticTable = value.into();
244 t.into()
245 }
246}
247
248impl From<DynamicTable> for Table {
249 fn from(value: DynamicTable) -> Self {
250 Self::Dynamic(value)
251 }
252}
253
254impl From<DynamicFuncTable> for Table {
255 fn from(value: DynamicFuncTable) -> Self {
256 let t: DynamicTable = value.into();
257 t.into()
258 }
259}
260
261impl From<DynamicGcRefTable> for Table {
262 fn from(value: DynamicGcRefTable) -> Self {
263 let t: DynamicTable = value.into();
264 t.into()
265 }
266}
267
268impl From<DynamicContTable> for Table {
269 fn from(value: DynamicContTable) -> Self {
270 let t: DynamicTable = value.into();
271 t.into()
272 }
273}
274
275pub(crate) fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
276 match ty.heap_type.top() {
277 WasmHeapTopType::Func => TableElementType::Func,
278 WasmHeapTopType::Any | WasmHeapTopType::Extern => TableElementType::GcRef,
279 WasmHeapTopType::Cont => TableElementType::Cont,
280 WasmHeapTopType::Exn => TableElementType::GcRef,
281 }
282}
283
284unsafe fn alloc_dynamic_table_elements<T>(len: usize) -> Result<Vec<Option<T>>> {
295 debug_assert!(
296 unsafe {
297 core::mem::MaybeUninit::<Option<T>>::zeroed()
298 .assume_init()
299 .is_none()
300 },
301 "null table elements are represented with zeroed memory"
302 );
303
304 if len == 0 {
305 return Ok(vec![]);
306 }
307
308 let align = mem::align_of::<Option<T>>();
309
310 let size = mem::size_of::<Option<T>>();
311 let size = size.next_multiple_of(align);
312 let size = size.checked_mul(len).unwrap();
313
314 let layout = Layout::from_size_align(size, align)?;
315
316 let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
317 ensure!(!ptr.is_null(), "failed to allocate memory for table");
318
319 let elems = unsafe { Vec::<Option<T>>::from_raw_parts(ptr.cast(), len, len) };
320 debug_assert!(elems.iter().all(|e| e.is_none()));
321
322 Ok(elems)
323}
324
325impl Table {
326 pub async fn new_dynamic(
328 ty: &wasmtime_environ::Table,
329 tunables: &Tunables,
330 limiter: Option<&mut StoreResourceLimiter<'_>>,
331 ) -> Result<Self> {
332 let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
333 match wasm_to_table_type(ty.ref_type) {
334 TableElementType::Func => Ok(Self::from(DynamicFuncTable {
335 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
336 maximum,
337 lazy_init: tunables.table_lazy_init,
338 })),
339 TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable {
340 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
341 maximum,
342 })),
343 TableElementType::Cont => Ok(Self::from(DynamicContTable {
344 elements: vec![None; minimum],
345 maximum,
346 })),
347 }
348 }
349
350 pub async unsafe fn new_static(
352 ty: &wasmtime_environ::Table,
353 tunables: &Tunables,
354 data: SendSyncPtr<[u8]>,
355 limiter: Option<&mut StoreResourceLimiter<'_>>,
356 ) -> Result<Self> {
357 let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
358 let size = minimum;
359 let max = maximum.unwrap_or(usize::MAX);
360
361 match wasm_to_table_type(ty.ref_type) {
362 TableElementType::Func => {
363 let len = {
364 let (before, data, after) = unsafe {
365 let data = data.as_non_null().as_ref();
366 data.align_to::<FuncTableElem>()
367 };
368 assert!(before.is_empty());
369 assert!(after.is_empty());
370 data.len()
371 };
372 ensure!(
373 usize::try_from(ty.limits.min).unwrap() <= len,
374 "initial table size of {} exceeds the pooling allocator's \
375 configured maximum table size of {len} elements",
376 ty.limits.min,
377 );
378 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
379 data.as_non_null().cast::<FuncTableElem>(),
380 cmp::min(len, max),
381 ));
382 Ok(Self::from(StaticFuncTable {
383 data,
384 size,
385 lazy_init: tunables.table_lazy_init,
386 }))
387 }
388 TableElementType::GcRef => {
389 let len = {
390 let (before, data, after) = unsafe {
391 let data = data.as_non_null().as_ref();
392 data.align_to::<Option<VMGcRef>>()
393 };
394 assert!(before.is_empty());
395 assert!(after.is_empty());
396 data.len()
397 };
398 ensure!(
399 usize::try_from(ty.limits.min).unwrap() <= len,
400 "initial table size of {} exceeds the pooling allocator's \
401 configured maximum table size of {len} elements",
402 ty.limits.min,
403 );
404 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
405 data.as_non_null().cast::<Option<VMGcRef>>(),
406 cmp::min(len, max),
407 ));
408 Ok(Self::from(StaticGcRefTable { data, size }))
409 }
410 TableElementType::Cont => {
411 let len = {
412 let (before, data, after) = unsafe {
413 let data = data.as_non_null().as_ref();
414 data.align_to::<ContTableElem>()
415 };
416 assert!(before.is_empty());
417 assert!(after.is_empty());
418 data.len()
419 };
420 ensure!(
421 usize::try_from(ty.limits.min).unwrap() <= len,
422 "initial table size of {} exceeds the pooling allocator's \
423 configured maximum table size of {len} elements",
424 ty.limits.min,
425 );
426 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
427 data.as_non_null().cast::<ContTableElem>(),
428 cmp::min(len, max),
429 ));
430 Ok(Self::from(StaticContTable { data, size }))
431 }
432 }
433 }
434
435 async fn limit_new(
439 ty: &wasmtime_environ::Table,
440 limiter: Option<&mut StoreResourceLimiter<'_>>,
441 ) -> Result<(usize, Option<usize>)> {
442 let absolute_max = usize::MAX;
445
446 let minimum = usize::try_from(ty.limits.min).ok();
449
450 let maximum = match (ty.limits.max, ty.idx_type) {
455 (Some(max), _) => usize::try_from(max).ok(),
456 (None, IndexType::I64) => usize::try_from(u64::MAX).ok(),
457 (None, IndexType::I32) => usize::try_from(u32::MAX).ok(),
458 };
459
460 if let Some(limiter) = limiter {
462 if !limiter
463 .table_growing(0, minimum.unwrap_or(absolute_max), maximum)
464 .await?
465 {
466 bail!(
467 "table minimum size of {} elements exceeds table limits",
468 ty.limits.min
469 );
470 }
471 }
472
473 let minimum = minimum.ok_or_else(|| {
476 format_err!(
477 "table minimum size of {} elements exceeds table limits",
478 ty.limits.min
479 )
480 })?;
481 Ok((minimum, maximum))
482 }
483
484 pub fn element_type(&self) -> TableElementType {
486 match self {
487 Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => {
488 TableElementType::Func
489 }
490 Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => {
491 TableElementType::GcRef
492 }
493 Table::Static(StaticTable::Cont(_)) | Table::Dynamic(DynamicTable::Cont(_)) => {
494 TableElementType::Cont
495 }
496 }
497 }
498
499 #[cfg(feature = "pooling-allocator")]
501 pub(crate) fn is_static(&self) -> bool {
502 matches!(self, Table::Static(_))
503 }
504
505 pub fn size(&self) -> usize {
507 match self {
508 Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size,
509 Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size,
510 Table::Static(StaticTable::Cont(StaticContTable { size, .. })) => *size,
511 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => elements.len(),
512 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
513 elements.len()
514 }
515 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => elements.len(),
516 }
517 }
518
519 pub fn maximum(&self) -> Option<usize> {
526 match self {
527 Table::Static(StaticTable::Cont(StaticContTable { data, .. })) => Some(data.len()),
528 Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => Some(data.len()),
529 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => Some(data.len()),
530 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum,
531 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum,
532 Table::Dynamic(DynamicTable::Cont(DynamicContTable { maximum, .. })) => *maximum,
533 }
534 }
535
536 pub fn fill_func(
544 &mut self,
545 dst: u64,
546 val: Option<NonNull<VMFuncRef>>,
547 len: u64,
548 ) -> Result<(), Trap> {
549 let range = self.validate_fill(dst, len)?;
550 let (funcrefs, lazy_init) = self.funcrefs_mut();
551 funcrefs[range].fill(MaybeTaggedFuncRef::from(val, lazy_init));
552 Ok(())
553 }
554
555 pub fn fill_gc_ref(
561 &mut self,
562 mut gc_store: Option<&mut GcStore>,
563 dst: u64,
564 val: Option<&VMGcRef>,
565 len: u64,
566 ) -> Result<(), Trap> {
567 let range = self.validate_fill(dst, len)?;
568
569 for slot in &mut self.gc_refs_mut()[range] {
571 GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), slot, val);
572 }
573
574 Ok(())
575 }
576 pub fn fill_cont(&mut self, dst: u64, val: Option<VMContObj>, len: u64) -> Result<(), Trap> {
578 let range = self.validate_fill(dst, len)?;
579 self.contrefs_mut()[range].fill(val);
580 Ok(())
581 }
582
583 fn validate_fill(&mut self, dst: u64, len: u64) -> Result<Range<usize>, Trap> {
584 let start = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
585 let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
586 let end = start
587 .checked_add(len)
588 .ok_or_else(|| Trap::TableOutOfBounds)?;
589
590 if end > self.size() {
591 return Err(Trap::TableOutOfBounds);
592 }
593 Ok(start..end)
594 }
595
596 pub async unsafe fn grow_func(
617 &mut self,
618 limiter: Option<&mut StoreResourceLimiter<'_>>,
619 delta: u64,
620 init_value: Option<SendSyncPtr<VMFuncRef>>,
621 ) -> Result<Option<usize>, Error> {
622 self._grow(delta, limiter, |me, base, len| {
623 me.fill_func(base, init_value.map(|p| p.as_non_null()), len)
624 })
625 .await
626 }
627
628 pub async unsafe fn grow_gc_ref(
630 &mut self,
631 limiter: Option<&mut StoreResourceLimiter<'_>>,
632 gc_store: Option<&mut GcStore>,
633 delta: u64,
634 init_value: Option<&VMGcRef>,
635 ) -> Result<Option<usize>, Error> {
636 self._grow(delta, limiter, |me, base, len| {
637 me.fill_gc_ref(gc_store, base, init_value, len)
638 })
639 .await
640 }
641
642 pub async unsafe fn grow_cont(
644 &mut self,
645 limiter: Option<&mut StoreResourceLimiter<'_>>,
646 delta: u64,
647 init_value: Option<VMContObj>,
648 ) -> Result<Option<usize>, Error> {
649 self._grow(delta, limiter, |me, base, len| {
650 me.fill_cont(base, init_value, len)
651 })
652 .await
653 }
654
655 async fn _grow(
656 &mut self,
657 delta: u64,
658 mut limiter: Option<&mut StoreResourceLimiter<'_>>,
659 fill: impl FnOnce(&mut Self, u64, u64) -> Result<(), Trap>,
660 ) -> Result<Option<usize>, Error> {
661 let old_size = self.size();
662
663 if delta == 0 {
666 return Ok(Some(old_size));
667 }
668 let delta = usize::try_from(delta).map_err(|_| Trap::TableOutOfBounds)?;
669
670 let new_size = match old_size.checked_add(delta) {
671 Some(s) => s,
672 None => {
673 if let Some(limiter) = limiter {
674 limiter
675 .table_grow_failed(format_err!("overflow calculating new table size"))?;
676 }
677 return Ok(None);
678 }
679 };
680
681 if let Some(limiter) = &mut limiter {
682 if !limiter
683 .table_growing(old_size, new_size, self.maximum())
684 .await?
685 {
686 return Ok(None);
687 }
688 }
689
690 if let Some(max) = self.maximum() {
694 if new_size > max {
695 if let Some(limiter) = limiter {
696 limiter.table_grow_failed(format_err!("Table maximum size exceeded"))?;
697 }
698 return Ok(None);
699 }
700 }
701
702 match self {
704 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
705 unsafe {
706 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
707 }
708 *size = new_size;
709 }
710 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
711 unsafe {
712 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
713 }
714 *size = new_size;
715 }
716 Table::Static(StaticTable::Cont(StaticContTable { data, size })) => {
717 unsafe {
718 debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
719 }
720 *size = new_size;
721 }
722
723 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
731 elements.resize(new_size, None);
732 }
733 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
734 elements.resize_with(new_size, || None);
735 }
736 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
737 elements.resize(new_size, None);
738 }
739 }
740
741 fill(
742 self,
743 u64::try_from(old_size).unwrap(),
744 u64::try_from(delta).unwrap(),
745 )
746 .expect("table should not be out of bounds");
747
748 Ok(Some(old_size))
749 }
750
751 pub fn get_func(&self, index: u64) -> Result<Option<NonNull<VMFuncRef>>, Trap> {
757 match self.get_func_maybe_init(index)? {
758 Some(elem) => Ok(elem),
759 None => panic!("function index should have been initialized"),
760 }
761 }
762
763 pub fn get_func_maybe_init(
767 &self,
768 index: u64,
769 ) -> Result<Option<Option<NonNull<VMFuncRef>>>, Trap> {
770 let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
771 let (funcrefs, lazy_init) = self.funcrefs();
772 Ok(funcrefs
773 .get(index)
774 .ok_or(Trap::TableOutOfBounds)?
775 .into_funcref(lazy_init))
776 }
777
778 pub fn get_gc_ref(&self, index: u64) -> Result<Option<&VMGcRef>, Trap> {
780 let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
781 let gcref = self.gc_refs().get(index).ok_or(Trap::TableOutOfBounds)?;
782 Ok(gcref.as_ref())
783 }
784
785 pub fn get_cont(&self, index: u64) -> Result<Option<VMContObj>, Trap> {
787 let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
788 let cont = self.contrefs().get(index).ok_or(Trap::TableOutOfBounds)?;
789 Ok(*cont)
790 }
791
792 pub fn set_func(&mut self, index: u64, elem: Option<NonNull<VMFuncRef>>) -> Result<(), Trap> {
803 let trap = Trap::TableOutOfBounds;
804 let index: usize = index.try_into().map_err(|_| trap)?;
805 let (funcrefs, lazy_init) = self.funcrefs_mut();
806 *funcrefs.get_mut(index).ok_or(trap)? = MaybeTaggedFuncRef::from(elem, lazy_init);
807 Ok(())
808 }
809
810 pub fn set_gc_ref(
812 &mut self,
813 store: Option<&mut GcStore>,
814 index: u64,
815 elem: Option<&VMGcRef>,
816 ) -> Result<(), Trap> {
817 let trap = Trap::TableOutOfBounds;
818 let index: usize = index.try_into().map_err(|_| trap)?;
819 GcStore::write_gc_ref_optional_store(
820 store,
821 self.gc_refs_mut().get_mut(index).ok_or(trap)?,
822 elem,
823 );
824 Ok(())
825 }
826
827 pub fn copy_to(
835 &self,
836 dst: &mut Table,
837 gc_store: Option<&mut GcStore>,
838 dst_index: u64,
839 src_index: u64,
840 len: u64,
841 ) -> Result<(), Trap> {
842 let (src_range, dst_range) = Table::validate_copy(self, dst, dst_index, src_index, len)?;
843 Self::copy_elements(gc_store, dst, self, dst_range, src_range);
844 Ok(())
845 }
846
847 pub fn copy_within(
855 &mut self,
856 gc_store: Option<&mut GcStore>,
857 dst_index: u64,
858 src_index: u64,
859 len: u64,
860 ) -> Result<(), Trap> {
861 let (src_range, dst_range) = Table::validate_copy(self, self, dst_index, src_index, len)?;
862 self.copy_elements_within(gc_store, dst_range, src_range);
863 Ok(())
864 }
865
866 fn validate_copy(
873 src: &Table,
874 dst: &Table,
875 dst_index: u64,
876 src_index: u64,
877 len: u64,
878 ) -> Result<(Range<usize>, Range<usize>), Trap> {
879 let src_index = usize::try_from(src_index).map_err(|_| Trap::TableOutOfBounds)?;
882 let dst_index = usize::try_from(dst_index).map_err(|_| Trap::TableOutOfBounds)?;
883 let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
884
885 if src_index.checked_add(len).map_or(true, |n| n > src.size())
886 || dst_index.checked_add(len).map_or(true, |m| m > dst.size())
887 {
888 return Err(Trap::TableOutOfBounds);
889 }
890
891 debug_assert!(
892 dst.element_type() == src.element_type(),
893 "table element type mismatch"
894 );
895
896 let src_range = src_index..src_index + len;
897 let dst_range = dst_index..dst_index + len;
898
899 Ok((src_range, dst_range))
900 }
901
902 pub fn vmtable(&mut self) -> VMTableDefinition {
904 match self {
905 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
906 VMTableDefinition {
907 base: data.cast().into(),
908 current_elements: *size,
909 }
910 }
911 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
912 VMTableDefinition {
913 base: data.cast().into(),
914 current_elements: *size,
915 }
916 }
917 Table::Static(StaticTable::Cont(StaticContTable { data, size })) => VMTableDefinition {
918 base: data.cast().into(),
919 current_elements: *size,
920 },
921 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
922 VMTableDefinition {
923 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
924 current_elements: elements.len(),
925 }
926 }
927 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
928 VMTableDefinition {
929 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
930 current_elements: elements.len(),
931 }
932 }
933 Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
934 VMTableDefinition {
935 base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
936 current_elements: elements.len(),
937 }
938 }
939 }
940 }
941
942 fn funcrefs(&self) -> (&[MaybeTaggedFuncRef], bool) {
943 assert_eq!(self.element_type(), TableElementType::Func);
944 match self {
945 Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
946 elements,
947 lazy_init,
948 ..
949 })) => (
950 unsafe { slice::from_raw_parts(elements.as_ptr().cast(), elements.len()) },
951 *lazy_init,
952 ),
953 Self::Static(StaticTable::Func(StaticFuncTable {
954 data,
955 size,
956 lazy_init,
957 })) => (
958 unsafe { slice::from_raw_parts(data.as_ptr().cast(), *size) },
959 *lazy_init,
960 ),
961 _ => unreachable!(),
962 }
963 }
964
965 fn funcrefs_mut(&mut self) -> (&mut [MaybeTaggedFuncRef], bool) {
966 assert_eq!(self.element_type(), TableElementType::Func);
967 match self {
968 Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
969 elements,
970 lazy_init,
971 ..
972 })) => (
973 unsafe { slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len()) },
974 *lazy_init,
975 ),
976 Self::Static(StaticTable::Func(StaticFuncTable {
977 data,
978 size,
979 lazy_init,
980 })) => (
981 unsafe { slice::from_raw_parts_mut(data.as_ptr().cast(), *size) },
982 *lazy_init,
983 ),
984 _ => unreachable!(),
985 }
986 }
987
988 fn gc_refs(&self) -> &[Option<VMGcRef>] {
989 assert_eq!(self.element_type(), TableElementType::GcRef);
990 match self {
991 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
992 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
993 &data.as_non_null().as_ref()[..*size]
994 },
995 _ => unreachable!(),
996 }
997 }
998
999 fn contrefs(&self) -> &[Option<VMContObj>] {
1000 assert_eq!(self.element_type(), TableElementType::Cont);
1001 match self {
1002 Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1003 slice::from_raw_parts(elements.as_ptr().cast(), elements.len())
1004 },
1005 Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1006 slice::from_raw_parts(data.as_ptr().cast(), *size)
1007 },
1008 _ => unreachable!(),
1009 }
1010 }
1011
1012 fn contrefs_mut(&mut self) -> &mut [Option<VMContObj>] {
1013 assert_eq!(self.element_type(), TableElementType::Cont);
1014 match self {
1015 Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1016 slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len())
1017 },
1018 Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1019 slice::from_raw_parts_mut(data.as_ptr().cast(), *size)
1020 },
1021 _ => unreachable!(),
1022 }
1023 }
1024
1025 pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] {
1029 assert_eq!(self.element_type(), TableElementType::GcRef);
1030 match self {
1031 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
1032 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
1033 &mut data.as_non_null().as_mut()[..*size]
1034 },
1035 _ => unreachable!(),
1036 }
1037 }
1038
1039 fn copy_elements(
1040 mut gc_store: Option<&mut GcStore>,
1041 dst_table: &mut Self,
1042 src_table: &Self,
1043 dst_range: Range<usize>,
1044 src_range: Range<usize>,
1045 ) {
1046 debug_assert!(!ptr::eq(dst_table, src_table));
1048
1049 let ty = dst_table.element_type();
1050
1051 match ty {
1052 TableElementType::Func => {
1053 let (dst_funcrefs, _lazy_init) = dst_table.funcrefs_mut();
1055 let (src_funcrefs, _lazy_init) = src_table.funcrefs();
1056 dst_funcrefs[dst_range].copy_from_slice(&src_funcrefs[src_range]);
1057 }
1058 TableElementType::GcRef => {
1059 assert_eq!(
1060 dst_range.end - dst_range.start,
1061 src_range.end - src_range.start
1062 );
1063 assert!(dst_range.end <= dst_table.gc_refs().len());
1064 assert!(src_range.end <= src_table.gc_refs().len());
1065 for (dst, src) in dst_range.zip(src_range) {
1066 GcStore::write_gc_ref_optional_store(
1067 gc_store.as_deref_mut(),
1068 &mut dst_table.gc_refs_mut()[dst],
1069 src_table.gc_refs()[src].as_ref(),
1070 );
1071 }
1072 }
1073 TableElementType::Cont => {
1074 dst_table.contrefs_mut()[dst_range]
1076 .copy_from_slice(&src_table.contrefs()[src_range]);
1077 }
1078 }
1079 }
1080
1081 fn copy_elements_within(
1082 &mut self,
1083 mut gc_store: Option<&mut GcStore>,
1084 dst_range: Range<usize>,
1085 src_range: Range<usize>,
1086 ) {
1087 assert_eq!(
1088 dst_range.end - dst_range.start,
1089 src_range.end - src_range.start
1090 );
1091
1092 if src_range.start == dst_range.start {
1094 return;
1095 }
1096
1097 let ty = self.element_type();
1098 match ty {
1099 TableElementType::Func => {
1100 let (funcrefs, _lazy_init) = self.funcrefs_mut();
1102 funcrefs.copy_within(src_range, dst_range.start);
1103 }
1104 TableElementType::GcRef => {
1105 let elements = self.gc_refs_mut();
1108 if dst_range.start < src_range.start {
1109 for (d, s) in dst_range.zip(src_range) {
1110 let (ds, ss) = elements.split_at_mut(s);
1111 let dst = &mut ds[d];
1112 let src = ss[0].as_ref();
1113 GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), dst, src);
1114 }
1115 } else {
1116 for (s, d) in src_range.rev().zip(dst_range.rev()) {
1117 let (ss, ds) = elements.split_at_mut(d);
1118 let dst = &mut ds[0];
1119 let src = ss[s].as_ref();
1120 GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), dst, src);
1121 }
1122 }
1123 }
1124 TableElementType::Cont => {
1125 self.contrefs_mut().copy_within(src_range, dst_range.start);
1127 }
1128 }
1129 }
1130}
1131
1132impl Default for Table {
1134 fn default() -> Self {
1135 Self::from(StaticFuncTable {
1136 data: SendSyncPtr::new(NonNull::from(&mut [])),
1137 size: 0,
1138 lazy_init: false,
1139 })
1140 }
1141}