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     /// 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     /// ```
new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table>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")]
new_async( mut store: impl AsContextMut, ty: TableType, init: Ref, ) -> Result<Table>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 
_new( store: &mut StoreOpaque, limiter: Option<&mut StoreResourceLimiter<'_>>, ty: TableType, init: Ref, ) -> Result<Table>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.
ty(&self, store: impl AsContext) -> TableType133     pub fn ty(&self, store: impl AsContext) -> TableType {
134         self._ty(store.as_context().0)
135     }
136 
_ty(&self, store: &StoreOpaque) -> TableType137     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`.
wasmtime_table<'a>( &self, store: &'a mut StoreOpaque, lazy_init_range: impl IntoIterator<Item = u64>, ) -> (&'a mut vm::Table, Option<&'a mut GcStore>)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.
get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref>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.
set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()>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 
set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()>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.
size(&self, store: impl AsContext) -> u64252     pub fn size(&self, store: impl AsContext) -> u64 {
253         self._size(store.as_context().0)
254     }
255 
_size(&self, store: &StoreOpaque) -> u64256     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.
grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64>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 
_grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64>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 doesn't own the table.
350     #[cfg(feature = "async")]
grow_async( &self, mut store: impl AsContextMut, delta: u64, init: Ref, ) -> Result<u64>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`.
copy( mut store: impl AsContextMut, dst_table: &Table, dst_index: u64, src_table: &Table, src_index: u64, len: u64, ) -> Result<()>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.
copy_raw( store: &mut StoreOpaque, dst_table: &Table, dst_index: u64, src_table: &Table, src_index: u64, len: u64, ) -> Result<(), Trap>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`.
fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()>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 
_fill( &self, store: &mut StoreOpaque, dst: u64, val: Ref, len: u64, ) -> Result<()>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")]
trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList)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 
from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table551     pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
552         Table { instance, index }
553     }
554 
wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table555     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 
vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport561     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 
comes_from_same_store(&self, store: &StoreOpaque) -> bool570     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
571         store.id() == self.instance.store_id()
572     }
573 
574     /// Returns a stable identifier for this table within its store.
575     ///
576     /// This allows distinguishing tables when introspecting them
577     /// e.g. via debug APIs.
578     #[cfg(feature = "debug")]
debug_index_in_store(&self) -> u64579     pub fn debug_index_in_store(&self) -> u64 {
580         u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
581     }
582 
583     /// Get a stable hash key for this table.
584     ///
585     /// Even if the same underlying table definition is added to the
586     /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
587     /// this hash key will be consistent across all of these tables.
588     #[cfg_attr(
589         not(test),
590         expect(dead_code, reason = "Not used yet, but added for consistency")
591     )]
hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_>592     pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
593         store[self.instance].table_ptr(self.index).as_ptr().addr()
594     }
595 }
596 
element_type(ty: &TableType) -> TableElementType597 fn element_type(ty: &TableType) -> TableElementType {
598     match ty.element().heap_type().top() {
599         HeapType::Func => TableElementType::Func,
600         HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
601         HeapType::Cont => TableElementType::Cont,
602         _ => unreachable!(),
603     }
604 }
605 
606 impl Ref {
into_table_func( self, store: &mut StoreOpaque, ty: &RefType, ) -> Result<Option<NonNull<VMFuncRef>>>607     fn into_table_func(
608         self,
609         store: &mut StoreOpaque,
610         ty: &RefType,
611     ) -> Result<Option<NonNull<VMFuncRef>>> {
612         self.ensure_matches_ty(store, &ty)
613             .context("type mismatch: value does not match table element type")?;
614 
615         match (self, ty.heap_type().top()) {
616             (Ref::Func(None), HeapType::Func) => {
617                 assert!(ty.is_nullable());
618                 Ok(None)
619             }
620             (Ref::Func(Some(f)), HeapType::Func) => {
621                 debug_assert!(
622                     f.comes_from_same_store(store),
623                     "checked in `ensure_matches_ty`"
624                 );
625                 Ok(Some(f.vm_func_ref(store)))
626             }
627 
628             _ => unreachable!("checked that the value matches the type above"),
629         }
630     }
631 
into_table_gc_ref<'a>( self, store: &'a mut AutoAssertNoGc<'_>, ty: &RefType, ) -> Result<Option<&'a VMGcRef>>632     fn into_table_gc_ref<'a>(
633         self,
634         store: &'a mut AutoAssertNoGc<'_>,
635         ty: &RefType,
636     ) -> Result<Option<&'a VMGcRef>> {
637         self.ensure_matches_ty(store, &ty)
638             .context("type mismatch: value does not match table element type")?;
639 
640         match (self, ty.heap_type().top()) {
641             (Ref::Extern(e), HeapType::Extern) => match e {
642                 None => {
643                     assert!(ty.is_nullable());
644                     Ok(None)
645                 }
646                 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
647             },
648 
649             (Ref::Any(a), HeapType::Any) => match a {
650                 None => {
651                     assert!(ty.is_nullable());
652                     Ok(None)
653                 }
654                 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
655             },
656 
657             (Ref::Exn(e), HeapType::Exn) => match e {
658                 None => {
659                     assert!(ty.is_nullable());
660                     Ok(None)
661                 }
662                 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
663             },
664 
665             _ => unreachable!("checked that the value matches the type above"),
666         }
667     }
668 }
669 
670 #[cfg(test)]
671 mod tests {
672     use super::*;
673     use crate::{Instance, Module, Store};
674 
675     #[test]
hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()>676     fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
677         let mut store = Store::<()>::default();
678         let module = Module::new(
679             store.engine(),
680             r#"
681                 (module
682                     (table (export "t") 1 1 externref)
683                 )
684             "#,
685         )?;
686         let instance = Instance::new(&mut store, &module, &[])?;
687 
688         // Each time we `get_table`, we call `Table::from_wasmtime` which adds
689         // a new entry to `StoreData`, so `t1` and `t2` will have different
690         // indices into `StoreData`.
691         let t1 = instance.get_table(&mut store, "t").unwrap();
692         let t2 = instance.get_table(&mut store, "t").unwrap();
693 
694         // That said, they really point to the same table.
695         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
696         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
697         let e = ExternRef::new(&mut store, 42)?;
698         t1.set(&mut store, 0, e.into())?;
699         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
700         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
701 
702         // And therefore their hash keys are the same.
703         assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
704 
705         // But the hash keys are different from different tables.
706         let instance2 = Instance::new(&mut store, &module, &[])?;
707         let t3 = instance2.get_table(&mut store, "t").unwrap();
708         assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
709 
710         Ok(())
711     }
712 
713     #[test]
grow_is_send()714     fn grow_is_send() {
715         fn _assert_send<T: Send>(_: T) {}
716         fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
717             _assert_send(table.grow(store, 0, init))
718         }
719     }
720 }
721