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