Skip to main content

wasmtime/runtime/externals/
table.rs

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