1 //! Memory management for tables.
2 //!
3 //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
4 
5 #![cfg_attr(feature = "gc", allow(irrefutable_let_patterns))]
6 
7 use crate::prelude::*;
8 use crate::runtime::vm::vmcontext::{VMFuncRef, VMTableDefinition};
9 use crate::runtime::vm::{GcStore, SendSyncPtr, Store, VMGcRef};
10 use core::cmp;
11 use core::ops::Range;
12 use core::ptr::{self, NonNull};
13 use core::slice;
14 use sptr::Strict;
15 use wasmtime_environ::{
16     TablePlan, TableStyle, Trap, WasmHeapTopType, WasmRefType, FUNCREF_INIT_BIT, FUNCREF_MASK,
17 };
18 
19 /// An element going into or coming out of a table.
20 ///
21 /// Table elements are stored as pointers and are default-initialized with
22 /// `ptr::null_mut`.
23 pub enum TableElement {
24     /// A `funcref`.
25     FuncRef(*mut VMFuncRef),
26 
27     /// A GC reference.
28     GcRef(Option<VMGcRef>),
29 
30     /// An uninitialized funcref value. This should never be exposed
31     /// beyond the `wasmtime` crate boundary; the upper-level code
32     /// (which has access to the info needed for lazy initialization)
33     /// will replace it when fetched.
34     UninitFunc,
35 }
36 
37 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
38 pub enum TableElementType {
39     Func,
40     GcRef,
41 }
42 
43 impl TableElementType {
44     fn matches(&self, val: &TableElement) -> bool {
45         match (val, self) {
46             (TableElement::FuncRef(_), TableElementType::Func) => true,
47             (TableElement::GcRef(_), TableElementType::GcRef) => true,
48             _ => false,
49         }
50     }
51 }
52 
53 // The usage of `*mut VMFuncRef` is safe w.r.t. thread safety, this just relies
54 // on thread-safety of `VMGcRef` itself.
55 unsafe impl Send for TableElement where VMGcRef: Send {}
56 unsafe impl Sync for TableElement where VMGcRef: Sync {}
57 
58 impl TableElement {
59     /// Consumes a table element into a pointer/reference, as it
60     /// exists outside the table itself. This strips off any tag bits
61     /// or other information that only lives inside the table.
62     ///
63     /// Can only be done to an initialized table element; lazy init
64     /// must occur first. (In other words, lazy values do not survive
65     /// beyond the table, as every table read path initializes them.)
66     ///
67     /// # Safety
68     ///
69     /// The same warnings as for `into_table_values()` apply.
70     pub(crate) unsafe fn into_func_ref_asserting_initialized(self) -> *mut VMFuncRef {
71         match self {
72             Self::FuncRef(e) => e,
73             Self::UninitFunc => panic!("Uninitialized table element value outside of table slot"),
74             Self::GcRef(_) => panic!("GC reference is not a function reference"),
75         }
76     }
77 
78     /// Indicates whether this value is the "uninitialized element"
79     /// value.
80     pub(crate) fn is_uninit(&self) -> bool {
81         match self {
82             Self::UninitFunc => true,
83             _ => false,
84         }
85     }
86 }
87 
88 impl From<*mut VMFuncRef> for TableElement {
89     fn from(f: *mut VMFuncRef) -> TableElement {
90         TableElement::FuncRef(f)
91     }
92 }
93 
94 impl From<Option<VMGcRef>> for TableElement {
95     fn from(x: Option<VMGcRef>) -> TableElement {
96         TableElement::GcRef(x)
97     }
98 }
99 
100 impl From<VMGcRef> for TableElement {
101     fn from(x: VMGcRef) -> TableElement {
102         TableElement::GcRef(Some(x))
103     }
104 }
105 
106 #[derive(Copy, Clone)]
107 #[repr(transparent)]
108 struct TaggedFuncRef(*mut VMFuncRef);
109 
110 impl TaggedFuncRef {
111     const UNINIT: TaggedFuncRef = TaggedFuncRef(ptr::null_mut());
112 
113     /// Converts the given `ptr`, a valid funcref pointer, into a tagged pointer
114     /// by adding in the `FUNCREF_INIT_BIT`.
115     fn from(ptr: *mut VMFuncRef, lazy_init: bool) -> Self {
116         if lazy_init {
117             let masked = Strict::map_addr(ptr, |a| a | FUNCREF_INIT_BIT);
118             TaggedFuncRef(masked)
119         } else {
120             TaggedFuncRef(ptr)
121         }
122     }
123 
124     /// Converts a tagged pointer into a `TableElement`, returning `UninitFunc`
125     /// for null (not a tagged value) or `FuncRef` for otherwise tagged values.
126     fn into_table_element(self, lazy_init: bool) -> TableElement {
127         let ptr = self.0;
128         if lazy_init && ptr.is_null() {
129             TableElement::UninitFunc
130         } else {
131             // Masking off the tag bit is harmless whether the table uses lazy
132             // init or not.
133             let unmasked = Strict::map_addr(ptr, |a| a & FUNCREF_MASK);
134             TableElement::FuncRef(unmasked)
135         }
136     }
137 }
138 
139 pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>;
140 
141 pub enum StaticTable {
142     Func(StaticFuncTable),
143     GcRef(StaticGcRefTable),
144 }
145 
146 impl From<StaticFuncTable> for StaticTable {
147     fn from(value: StaticFuncTable) -> Self {
148         Self::Func(value)
149     }
150 }
151 
152 impl From<StaticGcRefTable> for StaticTable {
153     fn from(value: StaticGcRefTable) -> Self {
154         Self::GcRef(value)
155     }
156 }
157 
158 pub struct StaticFuncTable {
159     /// Where data for this table is stored. The length of this list is the
160     /// maximum size of the table.
161     data: SendSyncPtr<[FuncTableElem]>,
162     /// The current size of the table.
163     size: u32,
164     /// Whether elements of this table are initialized lazily.
165     lazy_init: bool,
166 }
167 
168 pub struct StaticGcRefTable {
169     /// Where data for this table is stored. The length of this list is the
170     /// maximum size of the table.
171     data: SendSyncPtr<[Option<VMGcRef>]>,
172     /// The current size of the table.
173     size: u32,
174 }
175 
176 pub enum DynamicTable {
177     Func(DynamicFuncTable),
178     GcRef(DynamicGcRefTable),
179 }
180 
181 impl From<DynamicFuncTable> for DynamicTable {
182     fn from(value: DynamicFuncTable) -> Self {
183         Self::Func(value)
184     }
185 }
186 
187 impl From<DynamicGcRefTable> for DynamicTable {
188     fn from(value: DynamicGcRefTable) -> Self {
189         Self::GcRef(value)
190     }
191 }
192 
193 pub struct DynamicFuncTable {
194     /// Dynamically managed storage space for this table. The length of this
195     /// vector is the current size of the table.
196     elements: Vec<FuncTableElem>,
197     /// Maximum size that `elements` can grow to.
198     maximum: Option<u32>,
199     /// Whether elements of this table are initialized lazily.
200     lazy_init: bool,
201 }
202 
203 pub struct DynamicGcRefTable {
204     /// Dynamically managed storage space for this table. The length of this
205     /// vector is the current size of the table.
206     elements: Vec<Option<VMGcRef>>,
207     /// Maximum size that `elements` can grow to.
208     maximum: Option<u32>,
209 }
210 
211 /// Represents an instance's table.
212 pub enum Table {
213     /// A "static" table where storage space is managed externally, currently
214     /// used with the pooling allocator.
215     Static(StaticTable),
216     /// A "dynamic" table where table storage space is dynamically allocated via
217     /// `malloc` (aka Rust's `Vec`).
218     Dynamic(DynamicTable),
219 }
220 
221 impl From<StaticTable> for Table {
222     fn from(value: StaticTable) -> Self {
223         Self::Static(value)
224     }
225 }
226 
227 impl From<StaticFuncTable> for Table {
228     fn from(value: StaticFuncTable) -> Self {
229         let t: StaticTable = value.into();
230         t.into()
231     }
232 }
233 
234 impl From<StaticGcRefTable> for Table {
235     fn from(value: StaticGcRefTable) -> Self {
236         let t: StaticTable = value.into();
237         t.into()
238     }
239 }
240 
241 impl From<DynamicTable> for Table {
242     fn from(value: DynamicTable) -> Self {
243         Self::Dynamic(value)
244     }
245 }
246 
247 impl From<DynamicFuncTable> for Table {
248     fn from(value: DynamicFuncTable) -> Self {
249         let t: DynamicTable = value.into();
250         t.into()
251     }
252 }
253 
254 impl From<DynamicGcRefTable> for Table {
255     fn from(value: DynamicGcRefTable) -> Self {
256         let t: DynamicTable = value.into();
257         t.into()
258     }
259 }
260 
261 fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
262     match ty.heap_type.top() {
263         WasmHeapTopType::Func => TableElementType::Func,
264         WasmHeapTopType::Any | WasmHeapTopType::Extern => TableElementType::GcRef,
265     }
266 }
267 
268 impl Table {
269     /// Create a new dynamic (movable) table instance for the specified table plan.
270     pub fn new_dynamic(plan: &TablePlan, store: &mut dyn Store) -> Result<Self> {
271         Self::limit_new(plan, store)?;
272         let TableStyle::CallerChecksSignature { lazy_init } = plan.style;
273         match wasm_to_table_type(plan.table.wasm_ty) {
274             TableElementType::Func => Ok(Self::from(DynamicFuncTable {
275                 elements: vec![None; usize::try_from(plan.table.minimum).unwrap()],
276                 maximum: plan.table.maximum,
277                 lazy_init,
278             })),
279             TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable {
280                 elements: (0..usize::try_from(plan.table.minimum).unwrap())
281                     .map(|_| None)
282                     .collect(),
283                 maximum: plan.table.maximum,
284             })),
285         }
286     }
287 
288     /// Create a new static (immovable) table instance for the specified table plan.
289     pub unsafe fn new_static(
290         plan: &TablePlan,
291         data: SendSyncPtr<[u8]>,
292         store: &mut dyn Store,
293     ) -> Result<Self> {
294         Self::limit_new(plan, store)?;
295 
296         let size = plan.table.minimum;
297         let max = plan
298             .table
299             .maximum
300             .map_or(usize::MAX, |x| usize::try_from(x).unwrap());
301 
302         match wasm_to_table_type(plan.table.wasm_ty) {
303             TableElementType::Func => {
304                 let len = {
305                     let data = data.as_non_null().as_ref();
306                     let (before, data, after) = data.align_to::<FuncTableElem>();
307                     assert!(before.is_empty());
308                     assert!(after.is_empty());
309                     data.len()
310                 };
311                 ensure!(
312                     usize::try_from(plan.table.minimum).unwrap() <= len,
313                     "initial table size of {} exceeds the pooling allocator's \
314                      configured maximum table size of {len} elements",
315                     plan.table.minimum,
316                 );
317                 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
318                     data.as_non_null().cast::<FuncTableElem>(),
319                     cmp::min(len, max),
320                 ));
321                 let TableStyle::CallerChecksSignature { lazy_init } = plan.style;
322                 Ok(Self::from(StaticFuncTable {
323                     data,
324                     size,
325                     lazy_init,
326                 }))
327             }
328             TableElementType::GcRef => {
329                 let len = {
330                     let data = data.as_non_null().as_ref();
331                     let (before, data, after) = data.align_to::<Option<VMGcRef>>();
332                     assert!(before.is_empty());
333                     assert!(after.is_empty());
334                     data.len()
335                 };
336                 ensure!(
337                     usize::try_from(plan.table.minimum).unwrap() <= len,
338                     "initial table size of {} exceeds the pooling allocator's \
339                      configured maximum table size of {len} elements",
340                     plan.table.minimum,
341                 );
342                 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
343                     data.as_non_null().cast::<Option<VMGcRef>>(),
344                     cmp::min(len, max),
345                 ));
346                 Ok(Self::from(StaticGcRefTable { data, size }))
347             }
348         }
349     }
350 
351     fn limit_new(plan: &TablePlan, store: &mut dyn Store) -> Result<()> {
352         if !store.table_growing(0, plan.table.minimum, plan.table.maximum)? {
353             bail!(
354                 "table minimum size of {} elements exceeds table limits",
355                 plan.table.minimum
356             );
357         }
358         Ok(())
359     }
360 
361     /// Returns the type of the elements in this table.
362     pub fn element_type(&self) -> TableElementType {
363         match self {
364             Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => {
365                 TableElementType::Func
366             }
367             Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => {
368                 TableElementType::GcRef
369             }
370         }
371     }
372 
373     /// Returns whether or not the underlying storage of the table is "static".
374     #[cfg(feature = "pooling-allocator")]
375     pub(crate) fn is_static(&self) -> bool {
376         matches!(self, Table::Static(_))
377     }
378 
379     /// Returns the number of allocated elements.
380     pub fn size(&self) -> u32 {
381         match self {
382             Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size,
383             Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size,
384             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
385                 elements.len().try_into().unwrap()
386             }
387             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
388                 elements.len().try_into().unwrap()
389             }
390         }
391     }
392 
393     /// Returns the maximum number of elements at runtime.
394     ///
395     /// Returns `None` if the table is unbounded.
396     ///
397     /// The runtime maximum may not be equal to the maximum from the table's Wasm type
398     /// when it is being constrained by an instance allocator.
399     pub fn maximum(&self) -> Option<u32> {
400         match self {
401             Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => {
402                 Some(u32::try_from(data.len()).unwrap())
403             }
404             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => {
405                 Some(u32::try_from(data.len()).unwrap())
406             }
407             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum,
408             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum,
409         }
410     }
411 
412     /// Initializes the contents of this table to the specified function
413     ///
414     /// # Panics
415     ///
416     /// Panics if the table is not a function table.
417     pub fn init_func(
418         &mut self,
419         dst: u32,
420         items: impl ExactSizeIterator<Item = *mut VMFuncRef>,
421     ) -> Result<(), Trap> {
422         let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
423 
424         let (funcrefs, lazy_init) = self.funcrefs_mut();
425         let elements = funcrefs
426             .get_mut(dst..)
427             .and_then(|s| s.get_mut(..items.len()))
428             .ok_or(Trap::TableOutOfBounds)?;
429 
430         for (item, slot) in items.zip(elements) {
431             *slot = TaggedFuncRef::from(item, lazy_init);
432         }
433         Ok(())
434     }
435 
436     /// Fill `table[dst..]` with values from `items`
437     ///
438     /// Returns a trap error on out-of-bounds accesses.
439     pub fn init_gc_refs(
440         &mut self,
441         dst: u32,
442         items: impl ExactSizeIterator<Item = Option<VMGcRef>>,
443     ) -> Result<(), Trap> {
444         let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
445 
446         let elements = self
447             .gc_refs_mut()
448             .get_mut(dst..)
449             .and_then(|s| s.get_mut(..items.len()))
450             .ok_or(Trap::TableOutOfBounds)?;
451 
452         for (item, slot) in items.zip(elements) {
453             *slot = item;
454         }
455         Ok(())
456     }
457 
458     /// Fill `table[dst..dst + len]` with `val`.
459     ///
460     /// Returns a trap error on out-of-bounds accesses.
461     ///
462     /// # Panics
463     ///
464     /// Panics if `val` does not have a type that matches this table.
465     pub fn fill(
466         &mut self,
467         gc_store: &mut GcStore,
468         dst: u32,
469         val: TableElement,
470         len: u32,
471     ) -> Result<(), Trap> {
472         let start = dst as usize;
473         let end = start
474             .checked_add(len as usize)
475             .ok_or_else(|| Trap::TableOutOfBounds)?;
476 
477         if end > self.size() as usize {
478             return Err(Trap::TableOutOfBounds);
479         }
480 
481         match val {
482             TableElement::FuncRef(f) => {
483                 let (funcrefs, lazy_init) = self.funcrefs_mut();
484                 funcrefs[start..end].fill(TaggedFuncRef::from(f, lazy_init));
485             }
486             TableElement::GcRef(r) => {
487                 // Clone the init GC reference into each table slot.
488                 for slot in &mut self.gc_refs_mut()[start..end] {
489                     gc_store.write_gc_ref(slot, r.as_ref());
490                 }
491 
492                 // Drop the init GC reference, since we aren't holding onto this
493                 // reference anymore, only the clones in the table.
494                 if let Some(r) = r {
495                     gc_store.drop_gc_ref(r);
496                 }
497             }
498             TableElement::UninitFunc => {
499                 let (funcrefs, _lazy_init) = self.funcrefs_mut();
500                 funcrefs[start..end].fill(TaggedFuncRef::UNINIT);
501             }
502         }
503 
504         Ok(())
505     }
506 
507     /// Grow table by the specified amount of elements.
508     ///
509     /// Returns the previous size of the table if growth is successful.
510     ///
511     /// Returns `None` if table can't be grown by the specified amount of
512     /// elements, or if the `init_value` is the wrong kind of table element.
513     ///
514     /// # Panics
515     ///
516     /// Panics if `init_value` does not have a type that matches this table.
517     ///
518     /// # Unsafety
519     ///
520     /// Resizing the table can reallocate its internal elements buffer. This
521     /// table's instance's `VMContext` has raw pointers to the elements buffer
522     /// that are used by Wasm, and they need to be fixed up before we call into
523     /// Wasm again. Failure to do so will result in use-after-free inside Wasm.
524     ///
525     /// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates
526     /// this unsafety.
527     pub unsafe fn grow(
528         &mut self,
529         delta: u32,
530         init_value: TableElement,
531         store: &mut dyn Store,
532     ) -> Result<Option<u32>, Error> {
533         let old_size = self.size();
534 
535         // Don't try to resize the table if its size isn't changing, just return
536         // success.
537         if delta == 0 {
538             return Ok(Some(old_size));
539         }
540 
541         let new_size = match old_size.checked_add(delta) {
542             Some(s) => s,
543             None => {
544                 store.table_grow_failed(format_err!("overflow calculating new table size"))?;
545                 return Ok(None);
546             }
547         };
548 
549         if !store.table_growing(old_size, new_size, self.maximum())? {
550             return Ok(None);
551         }
552 
553         // The WebAssembly spec requires failing a `table.grow` request if
554         // it exceeds the declared limits of the table. We may have set lower
555         // limits in the instance allocator as well.
556         if let Some(max) = self.maximum() {
557             if new_size > max {
558                 store.table_grow_failed(format_err!("Table maximum size exceeded"))?;
559                 return Ok(None);
560             }
561         }
562 
563         debug_assert!(self.type_matches(&init_value));
564 
565         // First resize the storage and then fill with the init value
566         match self {
567             Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
568                 unsafe {
569                     debug_assert!(data.as_ref()[*size as usize..new_size as usize]
570                         .iter()
571                         .all(|x| x.is_none()));
572                 }
573                 *size = new_size;
574             }
575             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
576                 unsafe {
577                     debug_assert!(data.as_ref()[*size as usize..new_size as usize]
578                         .iter()
579                         .all(|x| x.is_none()));
580                 }
581                 *size = new_size;
582             }
583 
584             // These calls to `resize` could move the base address of
585             // `elements`. If this table's limits declare it to be fixed-size,
586             // then during AOT compilation we may have promised Cranelift that
587             // the table base address won't change, so it is allowed to optimize
588             // loading the base address. However, in that case the above checks
589             // that delta is non-zero and the new size doesn't exceed the
590             // maximum mean we can't get here.
591             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
592                 elements.resize(usize::try_from(new_size).unwrap(), None);
593             }
594             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
595                 elements.resize_with(usize::try_from(new_size).unwrap(), || None);
596             }
597         }
598 
599         self.fill(store.gc_store(), old_size, init_value, delta)
600             .expect("table should not be out of bounds");
601 
602         Ok(Some(old_size))
603     }
604 
605     /// Get reference to the specified element.
606     ///
607     /// Returns `None` if the index is out of bounds.
608     pub fn get(&self, gc_store: &mut GcStore, index: u32) -> Option<TableElement> {
609         let index = usize::try_from(index).ok()?;
610         match self.element_type() {
611             TableElementType::Func => {
612                 let (funcrefs, lazy_init) = self.funcrefs();
613                 funcrefs
614                     .get(index)
615                     .copied()
616                     .map(|e| e.into_table_element(lazy_init))
617             }
618             TableElementType::GcRef => self.gc_refs().get(index).map(|r| {
619                 let r = r.as_ref().map(|r| gc_store.clone_gc_ref(r));
620                 TableElement::GcRef(r)
621             }),
622         }
623     }
624 
625     /// Set reference to the specified element.
626     ///
627     /// # Errors
628     ///
629     /// Returns an error if `index` is out of bounds or if this table type does
630     /// not match the element type.
631     ///
632     /// # Panics
633     ///
634     /// Panics if `elem` is not of the right type for this table.
635     pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> {
636         let index = usize::try_from(index).map_err(|_| ())?;
637         match elem {
638             TableElement::FuncRef(f) => {
639                 let (funcrefs, lazy_init) = self.funcrefs_mut();
640                 *funcrefs.get_mut(index).ok_or(())? = TaggedFuncRef::from(f, lazy_init);
641             }
642             TableElement::UninitFunc => {
643                 let (funcrefs, _lazy_init) = self.funcrefs_mut();
644                 *funcrefs.get_mut(index).ok_or(())? = TaggedFuncRef::UNINIT;
645             }
646             TableElement::GcRef(e) => {
647                 *self.gc_refs_mut().get_mut(index).ok_or(())? = e;
648             }
649         }
650         Ok(())
651     }
652 
653     /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`.
654     ///
655     /// # Errors
656     ///
657     /// Returns an error if the range is out of bounds of either the source or
658     /// destination tables.
659     pub unsafe fn copy(
660         gc_store: &mut GcStore,
661         dst_table: *mut Self,
662         src_table: *mut Self,
663         dst_index: u32,
664         src_index: u32,
665         len: u32,
666     ) -> Result<(), Trap> {
667         // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
668 
669         if src_index
670             .checked_add(len)
671             .map_or(true, |n| n > (*src_table).size())
672             || dst_index
673                 .checked_add(len)
674                 .map_or(true, |m| m > (*dst_table).size())
675         {
676             return Err(Trap::TableOutOfBounds);
677         }
678 
679         debug_assert!(
680             (*dst_table).element_type() == (*src_table).element_type(),
681             "table element type mismatch"
682         );
683 
684         let src_range = src_index as usize..src_index as usize + len as usize;
685         let dst_range = dst_index as usize..dst_index as usize + len as usize;
686 
687         // Check if the tables are the same as we cannot mutably borrow and also borrow the same `RefCell`
688         if ptr::eq(dst_table, src_table) {
689             (*dst_table).copy_elements_within(gc_store, dst_range, src_range);
690         } else {
691             Self::copy_elements(gc_store, &mut *dst_table, &*src_table, dst_range, src_range);
692         }
693 
694         Ok(())
695     }
696 
697     /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
698     pub fn vmtable(&mut self) -> VMTableDefinition {
699         match self {
700             Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
701                 VMTableDefinition {
702                     base: data.as_ptr().cast(),
703                     current_elements: *size,
704                 }
705             }
706             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
707                 VMTableDefinition {
708                     base: data.as_ptr().cast(),
709                     current_elements: *size,
710                 }
711             }
712             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
713                 VMTableDefinition {
714                     base: elements.as_mut_ptr().cast(),
715                     current_elements: elements.len().try_into().unwrap(),
716                 }
717             }
718             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
719                 VMTableDefinition {
720                     base: elements.as_mut_ptr().cast(),
721                     current_elements: elements.len().try_into().unwrap(),
722                 }
723             }
724         }
725     }
726 
727     fn type_matches(&self, val: &TableElement) -> bool {
728         self.element_type().matches(val)
729     }
730 
731     fn funcrefs(&self) -> (&[TaggedFuncRef], bool) {
732         assert_eq!(self.element_type(), TableElementType::Func);
733         match self {
734             Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
735                 elements,
736                 lazy_init,
737                 ..
738             })) => (
739                 unsafe { slice::from_raw_parts(elements.as_ptr().cast(), elements.len()) },
740                 *lazy_init,
741             ),
742             Self::Static(StaticTable::Func(StaticFuncTable {
743                 data,
744                 size,
745                 lazy_init,
746             })) => (
747                 unsafe {
748                     slice::from_raw_parts(data.as_ptr().cast(), usize::try_from(*size).unwrap())
749                 },
750                 *lazy_init,
751             ),
752             _ => unreachable!(),
753         }
754     }
755 
756     fn funcrefs_mut(&mut self) -> (&mut [TaggedFuncRef], bool) {
757         assert_eq!(self.element_type(), TableElementType::Func);
758         match self {
759             Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
760                 elements,
761                 lazy_init,
762                 ..
763             })) => (
764                 unsafe { slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len()) },
765                 *lazy_init,
766             ),
767             Self::Static(StaticTable::Func(StaticFuncTable {
768                 data,
769                 size,
770                 lazy_init,
771             })) => (
772                 unsafe {
773                     slice::from_raw_parts_mut(data.as_ptr().cast(), usize::try_from(*size).unwrap())
774                 },
775                 *lazy_init,
776             ),
777             _ => unreachable!(),
778         }
779     }
780 
781     fn gc_refs(&self) -> &[Option<VMGcRef>] {
782         assert_eq!(self.element_type(), TableElementType::GcRef);
783         match self {
784             Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
785             Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
786                 &data.as_non_null().as_ref()[..usize::try_from(*size).unwrap()]
787             },
788             _ => unreachable!(),
789         }
790     }
791 
792     /// Get this table's GC references as a slice.
793     ///
794     /// Panics if this is not a table of GC references.
795     pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] {
796         assert_eq!(self.element_type(), TableElementType::GcRef);
797         match self {
798             Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
799             Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
800                 &mut data.as_non_null().as_mut()[..usize::try_from(*size).unwrap()]
801             },
802             _ => unreachable!(),
803         }
804     }
805 
806     fn copy_elements(
807         gc_store: &mut GcStore,
808         dst_table: &mut Self,
809         src_table: &Self,
810         dst_range: Range<usize>,
811         src_range: Range<usize>,
812     ) {
813         // This can only be used when copying between different tables
814         debug_assert!(!ptr::eq(dst_table, src_table));
815 
816         let ty = dst_table.element_type();
817 
818         match ty {
819             TableElementType::Func => {
820                 // `funcref` are `Copy`, so just do a mempcy
821                 let (dst_funcrefs, _lazy_init) = dst_table.funcrefs_mut();
822                 let (src_funcrefs, _lazy_init) = src_table.funcrefs();
823                 dst_funcrefs[dst_range].copy_from_slice(&src_funcrefs[src_range]);
824             }
825             TableElementType::GcRef => {
826                 assert_eq!(
827                     dst_range.end - dst_range.start,
828                     src_range.end - src_range.start
829                 );
830                 assert!(dst_range.end <= dst_table.gc_refs().len());
831                 assert!(src_range.end <= src_table.gc_refs().len());
832                 for (dst, src) in dst_range.zip(src_range) {
833                     gc_store.write_gc_ref(
834                         &mut dst_table.gc_refs_mut()[dst],
835                         src_table.gc_refs()[src].as_ref(),
836                     );
837                 }
838             }
839         }
840     }
841 
842     fn copy_elements_within(
843         &mut self,
844         gc_store: &mut GcStore,
845         dst_range: Range<usize>,
846         src_range: Range<usize>,
847     ) {
848         assert_eq!(
849             dst_range.end - dst_range.start,
850             src_range.end - src_range.start
851         );
852 
853         // This is a no-op.
854         if src_range.start == dst_range.start {
855             return;
856         }
857 
858         let ty = self.element_type();
859         match ty {
860             TableElementType::Func => {
861                 // `funcref` are `Copy`, so just do a memmove
862                 let (funcrefs, _lazy_init) = self.funcrefs_mut();
863                 funcrefs.copy_within(src_range, dst_range.start);
864             }
865             TableElementType::GcRef => {
866                 // We need to clone each `externref` while handling overlapping
867                 // ranges
868                 let elements = self.gc_refs_mut();
869                 if dst_range.start < src_range.start {
870                     for (d, s) in dst_range.zip(src_range) {
871                         let (ds, ss) = elements.split_at_mut(s);
872                         let dst = &mut ds[d];
873                         let src = ss[0].as_ref();
874                         gc_store.write_gc_ref(dst, src);
875                     }
876                 } else {
877                     for (s, d) in src_range.rev().zip(dst_range.rev()) {
878                         let (ss, ds) = elements.split_at_mut(d);
879                         let dst = &mut ds[0];
880                         let src = ss[s].as_ref();
881                         gc_store.write_gc_ref(dst, src);
882                     }
883                 }
884             }
885         }
886     }
887 }
888 
889 // The default table representation is an empty funcref table that cannot grow.
890 impl Default for Table {
891     fn default() -> Self {
892         Self::from(StaticFuncTable {
893             data: SendSyncPtr::new(NonNull::from(&mut [])),
894             size: 0,
895             lazy_init: false,
896         })
897     }
898 }
899