1 use crate::prelude::*;
2 use crate::runtime::RootedGcRefImpl;
3 use crate::runtime::vm::{self as runtime, GcStore, TableElementType, VMFuncRef, VMGcRef};
4 use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque};
5 use crate::trampoline::generate_table_export;
6 use crate::{
7     AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, HeapType, Ref, RefType, TableType,
8     Trap,
9 };
10 use core::iter;
11 use core::ptr::NonNull;
12 use wasmtime_environ::DefinedTableIndex;
13 
14 /// A WebAssembly `table`, or an array of values.
15 ///
16 /// Like [`Memory`][crate::Memory] a table is an indexed array of values, but
17 /// unlike [`Memory`][crate::Memory] it's an array of WebAssembly reference type
18 /// values rather than bytes. One of the most common usages of a table is a
19 /// function table for wasm modules (a `funcref` table), where each element has
20 /// the `ValType::FuncRef` type.
21 ///
22 /// A [`Table`] "belongs" to the store that it was originally created within
23 /// (either via [`Table::new`] or via instantiating a
24 /// [`Module`](crate::Module)). Operations on a [`Table`] only work with the
25 /// store it belongs to, and if another store is passed in by accident then
26 /// methods will panic.
27 #[derive(Copy, Clone, Debug)]
28 #[repr(C)] // here for the C API
29 pub struct Table {
30     instance: StoreInstanceId,
31     index: DefinedTableIndex,
32 }
33 
34 // Double-check that the C representation in `extern.h` matches our in-Rust
35 // representation here in terms of size/alignment/etc.
36 const _: () = {
37     #[repr(C)]
38     struct Tmp(u64, u32);
39     #[repr(C)]
40     struct C(Tmp, u32);
41     assert!(core::mem::size_of::<C>() == core::mem::size_of::<Table>());
42     assert!(core::mem::align_of::<C>() == core::mem::align_of::<Table>());
43     assert!(core::mem::offset_of!(Table, instance) == 0);
44 };
45 
46 impl Table {
47     /// Creates a new [`Table`] with the given parameters.
48     ///
49     /// * `store` - the owner of the resulting [`Table`]
50     /// * `ty` - the type of this table, containing both the element type as
51     ///   well as the initial size and maximum size, if any.
52     /// * `init` - the initial value to fill all table entries with, if the
53     ///   table starts with an initial size.
54     ///
55     /// # Errors
56     ///
57     /// Returns an error if `init` does not match the element type of the table,
58     /// or if `init` does not belong to the `store` provided.
59     ///
60     /// # Panics
61     ///
62     /// This function will panic when used with a [`Store`](`crate::Store`)
63     /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
64     /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`).
65     /// When using an async resource limiter, use [`Table::new_async`]
66     /// instead.
67     ///
68     /// # Examples
69     ///
70     /// ```
71     /// # use wasmtime::*;
72     /// # fn main() -> anyhow::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         Table::_new(store.as_context_mut().0, ty, init)
96     }
97 
98     /// Async variant of [`Table::new`]. You must use this variant with
99     /// [`Store`](`crate::Store`)s which have a
100     /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
101     ///
102     /// # Panics
103     ///
104     /// This function will panic when used with a non-async
105     /// [`Store`](`crate::Store`)
106     #[cfg(feature = "async")]
107     pub async fn new_async(
108         mut store: impl AsContextMut<Data: Send>,
109         ty: TableType,
110         init: Ref,
111     ) -> Result<Table> {
112         let mut store = store.as_context_mut();
113         assert!(
114             store.0.async_support(),
115             "cannot use `new_async` without enabling async support on the config"
116         );
117         store
118             .on_fiber(|store| Table::_new(store.0, ty, init))
119             .await?
120     }
121 
122     fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result<Table> {
123         let table = generate_table_export(store, &ty)?;
124         table._fill(store, 0, init, ty.minimum())?;
125         Ok(table)
126     }
127 
128     /// Returns the underlying type of this table, including its element type as
129     /// well as the maximum/minimum lower bounds.
130     ///
131     /// # Panics
132     ///
133     /// Panics if `store` does not own this table.
134     pub fn ty(&self, store: impl AsContext) -> TableType {
135         self._ty(store.as_context().0)
136     }
137 
138     fn _ty(&self, store: &StoreOpaque) -> TableType {
139         TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
140     }
141 
142     /// Returns the `runtime::Table` within `store` as well as the optional
143     /// `GcStore` in use within `store`.
144     ///
145     /// # Panics
146     ///
147     /// Panics if this table does not belong to `store`.
148     fn wasmtime_table<'a>(
149         &self,
150         store: &'a mut StoreOpaque,
151         lazy_init_range: impl IntoIterator<Item = u64>,
152     ) -> (&'a mut runtime::Table, Option<&'a mut GcStore>) {
153         self.instance.assert_belongs_to(store.id());
154         let (store, instance) = store.optional_gc_store_and_instance_mut(self.instance.instance());
155 
156         (
157             instance.get_defined_table_with_lazy_init(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     /// # Panics
275     ///
276     /// Panics if `store` does not own this table.
277     ///
278     /// This function will panic when used with a [`Store`](`crate::Store`)
279     /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
280     /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`)).
281     /// When using an async resource limiter, use [`Table::grow_async`]
282     /// instead.
283     pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
284         let store = store.as_context_mut().0;
285         let ty = self.ty(&store);
286         let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
287         // FIXME(#11179) shouldn't need to subvert the borrow checker
288         let table: *mut _ = table;
289         unsafe {
290             let result = match element_type(&ty) {
291                 TableElementType::Func => {
292                     let element = init.into_table_func(store, ty.element())?;
293                     (*table).grow_func(store, delta, element)?
294                 }
295                 TableElementType::GcRef => {
296                     // FIXME: `grow_gc_ref` shouldn't require the whole store
297                     // and should require `AutoAssertNoGc`. For now though we
298                     // know that table growth doesn't trigger GC so it should be
299                     // ok to create a copy of the GC reference even though it's
300                     // not tracked anywhere.
301                     let element = init
302                         .into_table_gc_ref(&mut AutoAssertNoGc::new(store), ty.element())?
303                         .map(|r| r.unchecked_copy());
304                     (*table).grow_gc_ref(store, delta, element.as_ref())?
305                 }
306                 // TODO(#10248) Required to support stack switching in the
307                 // embedder API.
308                 TableElementType::Cont => bail!("unimplemented table for cont"),
309             };
310             match result {
311                 Some(size) => {
312                     let vm = (*table).vmtable();
313                     store[self.instance].table_ptr(self.index).write(vm);
314                     // unwrap here should be ok because the runtime should always guarantee
315                     // that we can fit the table size in a 64-bit integer.
316                     Ok(u64::try_from(size).unwrap())
317                 }
318                 None => bail!("failed to grow table by `{}`", delta),
319             }
320         }
321     }
322 
323     /// Async variant of [`Table::grow`]. Required when using a
324     /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
325     ///
326     /// # Panics
327     ///
328     /// This function will panic when used with a non-async
329     /// [`Store`](`crate::Store`).
330     #[cfg(feature = "async")]
331     pub async fn grow_async(
332         &self,
333         mut store: impl AsContextMut<Data: Send>,
334         delta: u64,
335         init: Ref,
336     ) -> Result<u64> {
337         let mut store = store.as_context_mut();
338         assert!(
339             store.0.async_support(),
340             "cannot use `grow_async` without enabling async support on the config"
341         );
342         store
343             .on_fiber(|store| self.grow(store, delta, init))
344             .await?
345     }
346 
347     /// Copy `len` elements from `src_table[src_index..]` into
348     /// `dst_table[dst_index..]`.
349     ///
350     /// # Errors
351     ///
352     /// Returns an error if the range is out of bounds of either the source or
353     /// destination tables, or if the source table's element type does not match
354     /// the destination table's element type.
355     ///
356     /// # Panics
357     ///
358     /// Panics if `store` does not own either `dst_table` or `src_table`.
359     pub fn copy(
360         mut store: impl AsContextMut,
361         dst_table: &Table,
362         dst_index: u64,
363         src_table: &Table,
364         src_index: u64,
365         len: u64,
366     ) -> Result<()> {
367         let store = store.as_context_mut().0;
368 
369         let dst_ty = dst_table.ty(&store);
370         let src_ty = src_table.ty(&store);
371         src_ty
372             .element()
373             .ensure_matches(store.engine(), dst_ty.element())
374             .context(
375                 "type mismatch: source table's element type does not match \
376                  destination table's element type",
377             )?;
378 
379         // SAFETY: the the two tables have the same type, as type-checked above.
380         unsafe {
381             Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
382         }
383         Ok(())
384     }
385 
386     /// Copies the elements of `src_table` to `dst_table`.
387     ///
388     /// # Panics
389     ///
390     /// Panics if the either table doesn't belong to `store`.
391     ///
392     /// # Safety
393     ///
394     /// Requires that the two tables have previously been type-checked to have
395     /// the same type.
396     pub(crate) unsafe fn copy_raw(
397         store: &mut StoreOpaque,
398         dst_table: &Table,
399         dst_index: u64,
400         src_table: &Table,
401         src_index: u64,
402         len: u64,
403     ) -> Result<(), Trap> {
404         // Handle lazy initialization of the source table first before doing
405         // anything else.
406         let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
407         src_table.wasmtime_table(store, src_range);
408 
409         // validate `dst_table` belongs to `store`.
410         dst_table.wasmtime_table(store, iter::empty());
411 
412         // Figure out which of the three cases we're in:
413         //
414         // 1. Cross-instance table copy.
415         // 2. Intra-instance table copy.
416         // 3. Intra-table copy.
417         //
418         // We handle each of them slightly differently.
419         let src_instance = src_table.instance.instance();
420         let dst_instance = dst_table.instance.instance();
421         match (
422             src_instance == dst_instance,
423             src_table.index == dst_table.index,
424         ) {
425             // 1. Cross-instance table copy: split the mutable store borrow into
426             // two mutable instance borrows, get each instance's defined table,
427             // and do the copy.
428             (false, _) => {
429                 // SAFETY: accessing two instances mutably at the same time
430                 // requires only accessing defined entities on each instance
431                 // which is done below with `get_defined_*` methods.
432                 let (gc_store, [src_instance, dst_instance]) = unsafe {
433                     store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
434                 };
435                 src_instance.get_defined_table(src_table.index).copy_to(
436                     dst_instance.get_defined_table(dst_table.index),
437                     gc_store,
438                     dst_index,
439                     src_index,
440                     len,
441                 )
442             }
443 
444             // 2. Intra-instance, distinct-tables copy: split the mutable
445             // instance borrow into two distinct mutable table borrows and do
446             // the copy.
447             (true, false) => {
448                 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
449                 let [(_, src_table), (_, dst_table)] = instance
450                     .tables_mut()
451                     .get_disjoint_mut([src_table.index, dst_table.index])
452                     .unwrap();
453                 src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
454             }
455 
456             // 3. Intra-table copy: get the table and copy within it!
457             (true, true) => {
458                 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
459                 instance
460                     .get_defined_table(src_table.index)
461                     .copy_within(gc_store, dst_index, src_index, len)
462             }
463         }
464     }
465 
466     /// Fill `table[dst..(dst + len)]` with the given value.
467     ///
468     /// # Errors
469     ///
470     /// Returns an error if
471     ///
472     /// * `val` is not of the same type as this table's
473     ///   element type,
474     ///
475     /// * the region to be filled is out of bounds, or
476     ///
477     /// * `val` comes from a different `Store` from this table.
478     ///
479     /// # Panics
480     ///
481     /// Panics if `store` does not own either `dst_table` or `src_table`.
482     pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
483         self._fill(store.as_context_mut().0, dst, val, len)
484     }
485 
486     pub(crate) fn _fill(
487         &self,
488         store: &mut StoreOpaque,
489         dst: u64,
490         val: Ref,
491         len: u64,
492     ) -> Result<()> {
493         let ty = self._ty(&store);
494         match element_type(&ty) {
495             TableElementType::Func => {
496                 let val = val.into_table_func(store, ty.element())?;
497                 let (table, _) = self.wasmtime_table(store, iter::empty());
498                 table.fill_func(dst, val, len)?;
499             }
500             TableElementType::GcRef => {
501                 // Note that `val` is a `VMGcRef` temporarily read from the
502                 // store here, and blocking GC with `AutoAssertNoGc` should
503                 // ensure that it's not collected while being worked on here.
504                 let mut store = AutoAssertNoGc::new(store);
505                 let val = val.into_table_gc_ref(&mut store, ty.element())?;
506                 let val = val.map(|g| g.unchecked_copy());
507                 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
508                 table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
509             }
510             // TODO(#10248) Required to support stack switching in the embedder
511             // API.
512             TableElementType::Cont => bail!("unimplemented table for cont"),
513         }
514 
515         Ok(())
516     }
517 
518     #[cfg(feature = "gc")]
519     pub(crate) fn trace_roots(
520         &self,
521         store: &mut StoreOpaque,
522         gc_roots_list: &mut crate::runtime::vm::GcRootsList,
523     ) {
524         if !self
525             ._ty(store)
526             .element()
527             .is_vmgcref_type_and_points_to_object()
528         {
529             return;
530         }
531 
532         let (table, _) = self.wasmtime_table(store, iter::empty());
533         for gc_ref in table.gc_refs_mut() {
534             if let Some(gc_ref) = gc_ref {
535                 unsafe {
536                     gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
537                 }
538             }
539         }
540     }
541 
542     pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
543         Table { instance, index }
544     }
545 
546     pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
547         let module = store[self.instance].env_module();
548         let index = module.table_index(self.index);
549         &module.tables[index]
550     }
551 
552     pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMTableImport {
553         let instance = &store[self.instance];
554         crate::runtime::vm::VMTableImport {
555             from: instance.table_ptr(self.index).into(),
556             vmctx: instance.vmctx().into(),
557             index: self.index,
558         }
559     }
560 
561     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
562         store.id() == self.instance.store_id()
563     }
564 
565     /// Get a stable hash key for this table.
566     ///
567     /// Even if the same underlying table definition is added to the
568     /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
569     /// this hash key will be consistent across all of these tables.
570     #[cfg_attr(
571         not(test),
572         expect(dead_code, reason = "Not used yet, but added for consistency")
573     )]
574     pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
575         store[self.instance].table_ptr(self.index).as_ptr().addr()
576     }
577 }
578 
579 fn element_type(ty: &TableType) -> TableElementType {
580     match ty.element().heap_type().top() {
581         HeapType::Func => TableElementType::Func,
582         HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
583         HeapType::Cont => TableElementType::Cont,
584         _ => unreachable!(),
585     }
586 }
587 
588 impl Ref {
589     fn into_table_func(
590         self,
591         store: &mut StoreOpaque,
592         ty: &RefType,
593     ) -> Result<Option<NonNull<VMFuncRef>>> {
594         self.ensure_matches_ty(store, &ty)
595             .context("type mismatch: value does not match table element type")?;
596 
597         match (self, ty.heap_type().top()) {
598             (Ref::Func(None), HeapType::Func) => {
599                 assert!(ty.is_nullable());
600                 Ok(None)
601             }
602             (Ref::Func(Some(f)), HeapType::Func) => {
603                 debug_assert!(
604                     f.comes_from_same_store(store),
605                     "checked in `ensure_matches_ty`"
606                 );
607                 Ok(Some(f.vm_func_ref(store)))
608             }
609 
610             _ => unreachable!("checked that the value matches the type above"),
611         }
612     }
613 
614     fn into_table_gc_ref<'a>(
615         self,
616         store: &'a mut AutoAssertNoGc<'_>,
617         ty: &RefType,
618     ) -> Result<Option<&'a VMGcRef>> {
619         self.ensure_matches_ty(store, &ty)
620             .context("type mismatch: value does not match table element type")?;
621 
622         match (self, ty.heap_type().top()) {
623             (Ref::Extern(e), HeapType::Extern) => match e {
624                 None => {
625                     assert!(ty.is_nullable());
626                     Ok(None)
627                 }
628                 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
629             },
630 
631             (Ref::Any(a), HeapType::Any) => match a {
632                 None => {
633                     assert!(ty.is_nullable());
634                     Ok(None)
635                 }
636                 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
637             },
638 
639             (Ref::Exn(e), HeapType::Exn) => match e {
640                 None => {
641                     assert!(ty.is_nullable());
642                     Ok(None)
643                 }
644                 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
645             },
646 
647             _ => unreachable!("checked that the value matches the type above"),
648         }
649     }
650 }
651 
652 #[cfg(test)]
653 mod tests {
654     use super::*;
655     use crate::{Instance, Module, Store};
656 
657     #[test]
658     fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
659         let mut store = Store::<()>::default();
660         let module = Module::new(
661             store.engine(),
662             r#"
663                 (module
664                     (table (export "t") 1 1 externref)
665                 )
666             "#,
667         )?;
668         let instance = Instance::new(&mut store, &module, &[])?;
669 
670         // Each time we `get_table`, we call `Table::from_wasmtime` which adds
671         // a new entry to `StoreData`, so `t1` and `t2` will have different
672         // indices into `StoreData`.
673         let t1 = instance.get_table(&mut store, "t").unwrap();
674         let t2 = instance.get_table(&mut store, "t").unwrap();
675 
676         // That said, they really point to the same table.
677         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
678         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
679         let e = ExternRef::new(&mut store, 42)?;
680         t1.set(&mut store, 0, e.into())?;
681         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
682         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
683 
684         // And therefore their hash keys are the same.
685         assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
686 
687         // But the hash keys are different from different tables.
688         let instance2 = Instance::new(&mut store, &module, &[])?;
689         let t3 = instance2.get_table(&mut store, "t").unwrap();
690         assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
691 
692         Ok(())
693     }
694 }
695