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