Skip to main content

wasmtime/runtime/externals/
table.rs

1use crate::prelude::*;
2use crate::runtime::RootedGcRefImpl;
3use crate::runtime::vm::{
4    self, GcStore, SendSyncPtr, TableElementType, VMFuncRef, VMGcRef, VMStore,
5};
6use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque, StoreResourceLimiter};
7use crate::trampoline::generate_table_export;
8use crate::{
9    AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, HeapType, Ref, RefType,
10    StoreContextMut, TableType, Trap,
11};
12use core::iter;
13use core::ptr::NonNull;
14use wasmtime_environ::DefinedTableIndex;
15
16/// A WebAssembly `table`, or an array of values.
17///
18/// Like [`Memory`][crate::Memory] a table is an indexed array of values, but
19/// unlike [`Memory`][crate::Memory] it's an array of WebAssembly reference type
20/// values rather than bytes. One of the most common usages of a table is a
21/// function table for wasm modules (a `funcref` table), where each element has
22/// the `ValType::FuncRef` type.
23///
24/// A [`Table`] "belongs" to the store that it was originally created within
25/// (either via [`Table::new`] or via instantiating a
26/// [`Module`](crate::Module)). Operations on a [`Table`] only work with the
27/// store it belongs to, and if another store is passed in by accident then
28/// methods will panic.
29#[derive(Copy, Clone, Debug)]
30#[repr(C)] // here for the C API
31pub struct Table {
32    instance: StoreInstanceId,
33    index: DefinedTableIndex,
34}
35
36// Double-check that the C representation in `extern.h` matches our in-Rust
37// representation here in terms of size/alignment/etc.
38const _: () = {
39    #[repr(C)]
40    struct Tmp(u64, u32);
41    #[repr(C)]
42    struct C(Tmp, u32);
43    assert!(core::mem::size_of::<C>() == core::mem::size_of::<Table>());
44    assert!(core::mem::align_of::<C>() == core::mem::align_of::<Table>());
45    assert!(core::mem::offset_of!(Table, instance) == 0);
46};
47
48impl Table {
49    /// Creates a new [`Table`] with the given parameters.
50    ///
51    /// * `store` - the owner of the resulting [`Table`]
52    /// * `ty` - the type of this table, containing both the element type as
53    ///   well as the initial size and maximum size, if any.
54    /// * `init` - the initial value to fill all table entries with, if the
55    ///   table starts with an initial size.
56    ///
57    /// # Errors
58    ///
59    /// Returns an error if `init` does not match the element type of the table,
60    /// or if `init` does not belong to the `store` provided.
61    ///
62    /// This function will also return an error when used with a
63    /// [`Store`](`crate::Store`) which has a
64    /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) (see also:
65    /// [`Store::limiter_async`](`crate::Store::limiter_async`).  When using an
66    /// async resource limiter, use [`Table::new_async`] instead.
67    ///
68    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
69    /// memory allocation fails. See the `OutOfMemory` type's documentation for
70    /// details on Wasmtime's out-of-memory handling.
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// # use wasmtime::*;
76    /// # fn main() -> Result<()> {
77    /// let engine = Engine::default();
78    /// let mut store = Store::new(&engine, ());
79    ///
80    /// let ty = TableType::new(RefType::FUNCREF, 2, None);
81    /// let table = Table::new(&mut store, ty, Ref::Func(None))?;
82    ///
83    /// let module = Module::new(
84    ///     &engine,
85    ///     "(module
86    ///         (table (import \"\" \"\") 2 funcref)
87    ///         (func $f (result i32)
88    ///             i32.const 10)
89    ///         (elem (i32.const 0) $f)
90    ///     )"
91    /// )?;
92    ///
93    /// let instance = Instance::new(&mut store, &module, &[table.into()])?;
94    /// // ...
95    /// # Ok(())
96    /// # }
97    /// ```
98    pub fn new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table> {
99        let (mut limiter, store) = store
100            .as_context_mut()
101            .0
102            .validate_sync_resource_limiter_and_store_opaque()?;
103        vm::assert_ready(Table::_new(store, limiter.as_mut(), ty, init))
104    }
105
106    /// Async variant of [`Table::new`].
107    ///
108    /// You must use this variant with [`Store`](`crate::Store`)s which have a
109    /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
110    ///
111    /// # Errors
112    ///
113    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
114    /// memory allocation fails. See the `OutOfMemory` type's documentation for
115    /// details on Wasmtime's out-of-memory handling.
116    #[cfg(feature = "async")]
117    pub async fn new_async(
118        mut store: impl AsContextMut,
119        ty: TableType,
120        init: Ref,
121    ) -> Result<Table> {
122        let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
123        Table::_new(store, limiter.as_mut(), ty, init).await
124    }
125
126    async fn _new(
127        store: &mut StoreOpaque,
128        limiter: Option<&mut StoreResourceLimiter<'_>>,
129        ty: TableType,
130        init: Ref,
131    ) -> Result<Table> {
132        let table = generate_table_export(store, limiter, &ty).await?;
133        table._fill(store, 0, init, ty.minimum())?;
134        Ok(table)
135    }
136
137    /// Returns the underlying type of this table, including its element type as
138    /// well as the maximum/minimum lower bounds.
139    ///
140    /// # Panics
141    ///
142    /// Panics if `store` does not own this table.
143    pub fn ty(&self, store: impl AsContext) -> TableType {
144        self.ty_(store.as_context().0)
145    }
146
147    pub(crate) fn ty_(&self, store: &StoreOpaque) -> TableType {
148        TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
149    }
150
151    /// Returns the `vm::Table` within `store` as well as the optional
152    /// `GcStore` in use within `store`.
153    ///
154    /// # Panics
155    ///
156    /// Panics if this table does not belong to `store`.
157    fn wasmtime_table<'a>(
158        &self,
159        store: &'a mut StoreOpaque,
160        lazy_init_range: impl IntoIterator<Item = u64>,
161    ) -> (&'a mut vm::Table, Option<&'a mut GcStore>) {
162        self.instance.assert_belongs_to(store.id());
163        let (store, registry, instance) =
164            store.optional_gc_store_and_registry_and_instance_mut(self.instance.instance());
165
166        (
167            instance.get_defined_table_with_lazy_init(registry, self.index, lazy_init_range),
168            store,
169        )
170    }
171
172    /// Returns the table element value at `index`.
173    ///
174    /// Returns `None` if `index` is out of bounds.
175    ///
176    /// # Panics
177    ///
178    /// Panics if `store` does not own this table.
179    pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
180        let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
181        let (table, _gc_store) = self.wasmtime_table(&mut store, [index]);
182        match table.element_type() {
183            TableElementType::Func => {
184                let ptr = table.get_func(index).ok()?;
185                Some(
186                    // SAFETY: `store` owns this table, so therefore it owns all
187                    // functions within the table too.
188                    ptr.map(|p| unsafe { Func::from_vm_func_ref(store.id(), p) })
189                        .into(),
190                )
191            }
192            TableElementType::GcRef => {
193                let gc_ref = table
194                    .get_gc_ref(index)
195                    .ok()?
196                    .map(|r| r.unchecked_copy())
197                    .map(|r| store.clone_gc_ref(&r));
198                Some(match self.ty_(&store).element().heap_type().top() {
199                    HeapType::Extern => {
200                        Ref::Extern(gc_ref.map(|r| ExternRef::from_cloned_gc_ref(&mut store, r)))
201                    }
202                    HeapType::Any => {
203                        Ref::Any(gc_ref.map(|r| AnyRef::from_cloned_gc_ref(&mut store, r)))
204                    }
205                    HeapType::Exn => {
206                        Ref::Exn(gc_ref.map(|r| ExnRef::from_cloned_gc_ref(&mut store, r)))
207                    }
208                    _ => unreachable!(),
209                })
210            }
211            // TODO(#10248) Required to support stack switching in the embedder
212            // API.
213            TableElementType::Cont => panic!("unimplemented table for cont"),
214        }
215    }
216
217    /// Writes the `val` provided into `index` within this table.
218    ///
219    /// # Errors
220    ///
221    /// Returns an error if `index` is out of bounds, if `val` does not have
222    /// the right type to be stored in this table, or if `val` belongs to a
223    /// different store.
224    ///
225    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
226    /// memory allocation fails. See the `OutOfMemory` type's documentation for
227    /// details on Wasmtime's out-of-memory handling.
228    ///
229    /// # Panics
230    ///
231    /// Panics if `store` does not own this table.
232    pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
233        self.set_(store.as_context_mut().0, index, val)
234    }
235
236    pub(crate) fn set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()> {
237        let ty = self.ty_(store);
238        match element_type(&ty) {
239            TableElementType::Func => {
240                let element = val.into_table_func(store, ty.element())?;
241                let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
242                table.set_func(index, element)?;
243            }
244            TableElementType::GcRef => {
245                let mut store = AutoAssertNoGc::new(store);
246                let element = val.into_table_gc_ref(&mut store, ty.element())?;
247                // Note that `unchecked_copy` should be ok as we're under an
248                // `AutoAssertNoGc` which means that despite this not being
249                // rooted we don't have to worry about it going away.
250                let element = element.map(|r| r.unchecked_copy());
251                let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
252                table.set_gc_ref(gc_store, index, element.as_ref())?;
253            }
254            // TODO(#10248) Required to support stack switching in the embedder
255            // API.
256            TableElementType::Cont => bail!("unimplemented table for cont"),
257        }
258        Ok(())
259    }
260
261    /// Returns the current size of this table.
262    ///
263    /// # Panics
264    ///
265    /// Panics if `store` does not own this table.
266    pub fn size(&self, store: impl AsContext) -> u64 {
267        self.size_(store.as_context().0)
268    }
269
270    pub(crate) fn size_(&self, store: &StoreOpaque) -> u64 {
271        // unwrap here should be ok because the runtime should always guarantee
272        // that we can fit the number of elements in a 64-bit integer.
273        u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
274    }
275
276    /// Grows the size of this table by `delta` more elements, initialization
277    /// all new elements to `init`.
278    ///
279    /// Returns the previous size of this table if successful.
280    ///
281    /// # Errors
282    ///
283    /// Returns an error if the table cannot be grown by `delta`, for example
284    /// if it would cause the table to exceed its maximum size. Also returns an
285    /// error if `init` is not of the right type or if `init` does not belong to
286    /// `store`.
287    ///
288    /// This function also returns an error when used with a
289    /// [`Store`](`crate::Store`) which has a
290    /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) (see also:
291    /// [`Store::limiter_async`](`crate::Store::limiter_async`)).  When using an
292    /// async resource limiter, use [`Table::grow_async`] instead.
293    ///
294    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
295    /// memory allocation fails. See the `OutOfMemory` type's documentation for
296    /// details on Wasmtime's out-of-memory handling.
297    ///
298    /// # Panics
299    ///
300    /// Panics if `store` does not own this table.
301    pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
302        let store = store.as_context_mut();
303        store.0.validate_sync_resource_limiter_and_store_opaque()?;
304        vm::assert_ready(self._grow(store, delta, init))
305    }
306
307    async fn _grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64> {
308        let store = store.0;
309        let ty = self.ty(&store);
310        let (mut limiter, store) = store.resource_limiter_and_store_opaque();
311        let limiter = limiter.as_mut();
312        let result = match element_type(&ty) {
313            TableElementType::Func => {
314                let element = init
315                    .into_table_func(store, ty.element())?
316                    .map(SendSyncPtr::new);
317                self.instance
318                    .get_mut(store)
319                    .defined_table_grow(self.index, async |table| {
320                        // SAFETY: in the context of `defined_table_grow` this
321                        // is safe to call as it'll update the internal table
322                        // pointer in the instance.
323                        unsafe { table.grow_func(limiter, delta, element).await }
324                    })
325                    .await?
326            }
327            TableElementType::GcRef => {
328                let mut store = AutoAssertNoGc::new(store);
329                let element = init
330                    .into_table_gc_ref(&mut store, ty.element())?
331                    .map(|r| r.unchecked_copy());
332                let (gc_store, instance) = self.instance.get_with_gc_store_mut(&mut store);
333                instance
334                    .defined_table_grow(self.index, async |table| {
335                        // SAFETY: in the context of `defined_table_grow` this
336                        // is safe to call as it'll update the internal table
337                        // pointer in the instance.
338                        unsafe {
339                            table
340                                .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
341                                .await
342                        }
343                    })
344                    .await?
345            }
346            // TODO(#10248) Required to support stack switching in the
347            // embedder API.
348            TableElementType::Cont => bail!("unimplemented table for cont"),
349        };
350        match result {
351            Some(size) => {
352                // unwrap here should be ok because the runtime should always
353                // guarantee that we can fit the table size in a 64-bit integer.
354                Ok(u64::try_from(size).unwrap())
355            }
356            None => bail!("failed to grow table by `{delta}`"),
357        }
358    }
359
360    /// Async variant of [`Table::grow`].
361    ///
362    /// Required when using a
363    /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
364    ///
365    /// # Errors
366    ///
367    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
368    /// memory allocation fails. See the `OutOfMemory` type's documentation for
369    /// details on Wasmtime's out-of-memory handling.
370    ///
371    /// # Panics
372    ///
373    /// This function will panic when if the store doesn't own the table.
374    #[cfg(feature = "async")]
375    pub async fn grow_async(
376        &self,
377        mut store: impl AsContextMut,
378        delta: u64,
379        init: Ref,
380    ) -> Result<u64> {
381        self._grow(store.as_context_mut(), delta, init).await
382    }
383
384    /// Copy `len` elements from `src_table[src_index..]` into
385    /// `dst_table[dst_index..]`.
386    ///
387    /// # Errors
388    ///
389    /// Returns an error if the range is out of bounds of either the source or
390    /// destination tables, or if the source table's element type does not match
391    /// the destination table's element type.
392    ///
393    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
394    /// memory allocation fails. See the `OutOfMemory` type's documentation for
395    /// details on Wasmtime's out-of-memory handling.
396    ///
397    /// # Panics
398    ///
399    /// Panics if `store` does not own either `dst_table` or `src_table`.
400    pub fn copy(
401        mut store: impl AsContextMut,
402        dst_table: &Table,
403        dst_index: u64,
404        src_table: &Table,
405        src_index: u64,
406        len: u64,
407    ) -> Result<()> {
408        let store = store.as_context_mut().0;
409
410        let dst_ty = dst_table.ty(&store);
411        let src_ty = src_table.ty(&store);
412        src_ty
413            .element()
414            .ensure_matches(store.engine(), dst_ty.element())
415            .context(
416                "type mismatch: source table's element type does not match \
417                 destination table's element type",
418            )?;
419
420        // SAFETY: the the two tables have the same type, as type-checked above.
421        unsafe {
422            Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
423        }
424        Ok(())
425    }
426
427    /// Copies the elements of `src_table` to `dst_table`.
428    ///
429    /// # Panics
430    ///
431    /// Panics if the either table doesn't belong to `store`.
432    ///
433    /// # Safety
434    ///
435    /// Requires that the two tables have previously been type-checked to have
436    /// the same type.
437    pub(crate) unsafe fn copy_raw(
438        store: &mut StoreOpaque,
439        dst_table: &Table,
440        dst_index: u64,
441        src_table: &Table,
442        src_index: u64,
443        len: u64,
444    ) -> Result<(), Trap> {
445        // Handle lazy initialization of the source table first before doing
446        // anything else.
447        let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
448        src_table.wasmtime_table(store, src_range);
449
450        // validate `dst_table` belongs to `store`.
451        dst_table.wasmtime_table(store, iter::empty());
452
453        // Figure out which of the three cases we're in:
454        //
455        // 1. Cross-instance table copy.
456        // 2. Intra-instance table copy.
457        // 3. Intra-table copy.
458        //
459        // We handle each of them slightly differently.
460        let src_instance = src_table.instance.instance();
461        let dst_instance = dst_table.instance.instance();
462        match (
463            src_instance == dst_instance,
464            src_table.index == dst_table.index,
465        ) {
466            // 1. Cross-instance table copy: split the mutable store borrow into
467            // two mutable instance borrows, get each instance's defined table,
468            // and do the copy.
469            (false, _) => {
470                // SAFETY: accessing two instances mutably at the same time
471                // requires only accessing defined entities on each instance
472                // which is done below with `get_defined_*` methods.
473                let (gc_store, [src_instance, dst_instance]) = unsafe {
474                    store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
475                };
476                src_instance.get_defined_table(src_table.index).copy_to(
477                    dst_instance.get_defined_table(dst_table.index),
478                    gc_store,
479                    dst_index,
480                    src_index,
481                    len,
482                )
483            }
484
485            // 2. Intra-instance, distinct-tables copy: split the mutable
486            // instance borrow into two distinct mutable table borrows and do
487            // the copy.
488            (true, false) => {
489                let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
490                let [(_, src_table), (_, dst_table)] = instance
491                    .tables_mut()
492                    .get_disjoint_mut([src_table.index, dst_table.index])
493                    .unwrap();
494                src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
495            }
496
497            // 3. Intra-table copy: get the table and copy within it!
498            (true, true) => {
499                let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
500                instance
501                    .get_defined_table(src_table.index)
502                    .copy_within(gc_store, dst_index, src_index, len)
503            }
504        }
505    }
506
507    /// Fill `table[dst..(dst + len)]` with the given value.
508    ///
509    /// # Errors
510    ///
511    /// Returns an error if
512    ///
513    /// * `val` is not of the same type as this table's
514    ///   element type,
515    ///
516    /// * the region to be filled is out of bounds, or
517    ///
518    /// * `val` comes from a different `Store` from this table.
519    ///
520    /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
521    /// memory allocation fails. See the `OutOfMemory` type's documentation for
522    /// details on Wasmtime's out-of-memory handling.
523    ///
524    /// # Panics
525    ///
526    /// Panics if `store` does not own either `dst_table` or `src_table`.
527    pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
528        self._fill(store.as_context_mut().0, dst, val, len)
529    }
530
531    pub(crate) fn _fill(
532        &self,
533        store: &mut StoreOpaque,
534        dst: u64,
535        val: Ref,
536        len: u64,
537    ) -> Result<()> {
538        let ty = self.ty_(&store);
539        match element_type(&ty) {
540            TableElementType::Func => {
541                let val = val.into_table_func(store, ty.element())?;
542                let (table, _) = self.wasmtime_table(store, iter::empty());
543                table.fill_func(dst, val, len)?;
544            }
545            TableElementType::GcRef => {
546                // Note that `val` is a `VMGcRef` temporarily read from the
547                // store here, and blocking GC with `AutoAssertNoGc` should
548                // ensure that it's not collected while being worked on here.
549                let mut store = AutoAssertNoGc::new(store);
550                let val = val.into_table_gc_ref(&mut store, ty.element())?;
551                let val = val.map(|g| g.unchecked_copy());
552                let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
553                table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
554            }
555            // TODO(#10248) Required to support stack switching in the embedder
556            // API.
557            TableElementType::Cont => bail!("unimplemented table for cont"),
558        }
559
560        Ok(())
561    }
562
563    #[cfg(feature = "gc")]
564    pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
565        if !self
566            .ty_(store)
567            .element()
568            .is_vmgcref_type_and_points_to_object()
569        {
570            return;
571        }
572
573        let (table, _) = self.wasmtime_table(store, iter::empty());
574        for gc_ref in table.gc_refs_mut() {
575            if let Some(gc_ref) = gc_ref {
576                unsafe {
577                    gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm table element");
578                }
579            }
580        }
581    }
582
583    pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
584        Table { instance, index }
585    }
586
587    pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
588        let module = store[self.instance].env_module();
589        let index = module.table_index(self.index);
590        &module.tables[index]
591    }
592
593    pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport {
594        let instance = &store[self.instance];
595        vm::VMTableImport {
596            from: instance.table_ptr(self.index).into(),
597            vmctx: instance.vmctx().into(),
598            index: self.index,
599        }
600    }
601
602    pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
603        store.id() == self.instance.store_id()
604    }
605
606    /// Returns a stable identifier for this table within its store.
607    ///
608    /// This allows distinguishing tables when introspecting them
609    /// e.g. via debug APIs.
610    #[cfg(feature = "debug")]
611    pub fn debug_index_in_store(&self) -> u64 {
612        u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
613    }
614
615    /// Get a stable hash key for this table.
616    ///
617    /// Even if the same underlying table definition is added to the
618    /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
619    /// this hash key will be consistent across all of these tables.
620    #[cfg_attr(
621        not(test),
622        expect(dead_code, reason = "Not used yet, but added for consistency")
623    )]
624    pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
625        store[self.instance].table_ptr(self.index).as_ptr().addr()
626    }
627}
628
629fn element_type(ty: &TableType) -> TableElementType {
630    match ty.element().heap_type().top() {
631        HeapType::Func => TableElementType::Func,
632        HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
633        HeapType::Cont => TableElementType::Cont,
634        _ => unreachable!(),
635    }
636}
637
638impl Ref {
639    fn into_table_func(
640        self,
641        store: &mut StoreOpaque,
642        ty: &RefType,
643    ) -> Result<Option<NonNull<VMFuncRef>>> {
644        self.ensure_matches_ty(store, &ty)
645            .context("type mismatch: value does not match table element type")?;
646
647        match (self, ty.heap_type().top()) {
648            (Ref::Func(None), HeapType::Func) => {
649                assert!(ty.is_nullable());
650                Ok(None)
651            }
652            (Ref::Func(Some(f)), HeapType::Func) => {
653                debug_assert!(
654                    f.comes_from_same_store(store),
655                    "checked in `ensure_matches_ty`"
656                );
657                Ok(Some(f.vm_func_ref(store)))
658            }
659
660            _ => unreachable!("checked that the value matches the type above"),
661        }
662    }
663
664    fn into_table_gc_ref<'a>(
665        self,
666        store: &'a mut AutoAssertNoGc<'_>,
667        ty: &RefType,
668    ) -> Result<Option<&'a VMGcRef>> {
669        self.ensure_matches_ty(store, &ty)
670            .context("type mismatch: value does not match table element type")?;
671
672        match (self, ty.heap_type().top()) {
673            (Ref::Extern(e), HeapType::Extern) => match e {
674                None => {
675                    assert!(ty.is_nullable());
676                    Ok(None)
677                }
678                Some(e) => Ok(Some(e.try_gc_ref(store)?)),
679            },
680
681            (Ref::Any(a), HeapType::Any) => match a {
682                None => {
683                    assert!(ty.is_nullable());
684                    Ok(None)
685                }
686                Some(a) => Ok(Some(a.try_gc_ref(store)?)),
687            },
688
689            (Ref::Exn(e), HeapType::Exn) => match e {
690                None => {
691                    assert!(ty.is_nullable());
692                    Ok(None)
693                }
694                Some(e) => Ok(Some(e.try_gc_ref(store)?)),
695            },
696
697            _ => unreachable!("checked that the value matches the type above"),
698        }
699    }
700}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705    use crate::{Instance, Module, Store};
706
707    #[test]
708    fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
709        let mut store = Store::<()>::default();
710        let module = Module::new(
711            store.engine(),
712            r#"
713                (module
714                    (table (export "t") 1 1 externref)
715                )
716            "#,
717        )?;
718        let instance = Instance::new(&mut store, &module, &[])?;
719
720        // Each time we `get_table`, we call `Table::from_wasmtime` which adds
721        // a new entry to `StoreData`, so `t1` and `t2` will have different
722        // indices into `StoreData`.
723        let t1 = instance.get_table(&mut store, "t").unwrap();
724        let t2 = instance.get_table(&mut store, "t").unwrap();
725
726        // That said, they really point to the same table.
727        assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
728        assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
729        let e = ExternRef::new(&mut store, 42)?;
730        t1.set(&mut store, 0, e.into())?;
731        assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
732        assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
733
734        // And therefore their hash keys are the same.
735        assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
736
737        // But the hash keys are different from different tables.
738        let instance2 = Instance::new(&mut store, &module, &[])?;
739        let t3 = instance2.get_table(&mut store, "t").unwrap();
740        assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
741
742        Ok(())
743    }
744
745    #[test]
746    fn grow_is_send() {
747        fn _assert_send<T: Send>(_: T) {}
748        fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
749            _assert_send(table.grow(store, 0, init))
750        }
751    }
752}