wasmtime_runtime/
table.rs

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