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