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