1 use crate::prelude::*;
2 use crate::runtime::vm::{self as runtime, GcStore};
3 use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque};
4 use crate::trampoline::generate_table_export;
5 use crate::{AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, Ref, TableType};
6 use core::iter;
7 use wasmtime_environ::DefinedTableIndex;
8 
9 /// A WebAssembly `table`, or an array of values.
10 ///
11 /// Like [`Memory`][crate::Memory] a table is an indexed array of values, but
12 /// unlike [`Memory`][crate::Memory] it's an array of WebAssembly reference type
13 /// values rather than bytes. One of the most common usages of a table is a
14 /// function table for wasm modules (a `funcref` table), where each element has
15 /// the `ValType::FuncRef` type.
16 ///
17 /// A [`Table`] "belongs" to the store that it was originally created within
18 /// (either via [`Table::new`] or via instantiating a
19 /// [`Module`](crate::Module)). Operations on a [`Table`] only work with the
20 /// store it belongs to, and if another store is passed in by accident then
21 /// methods will panic.
22 #[derive(Copy, Clone, Debug)]
23 #[repr(C)] // here for the C API
24 pub struct Table {
25     instance: StoreInstanceId,
26     index: DefinedTableIndex,
27 }
28 
29 // Double-check that the C representation in `extern.h` matches our in-Rust
30 // representation here in terms of size/alignment/etc.
31 const _: () = {
32     #[repr(C)]
33     struct Tmp(u64, u32);
34     #[repr(C)]
35     struct C(Tmp, u32);
36     assert!(core::mem::size_of::<C>() == core::mem::size_of::<Table>());
37     assert!(core::mem::align_of::<C>() == core::mem::align_of::<Table>());
38     assert!(core::mem::offset_of!(Table, instance) == 0);
39 };
40 
41 impl Table {
42     /// Creates a new [`Table`] with the given parameters.
43     ///
44     /// * `store` - the owner of the resulting [`Table`]
45     /// * `ty` - the type of this table, containing both the element type as
46     ///   well as the initial size and maximum size, if any.
47     /// * `init` - the initial value to fill all table entries with, if the
48     ///   table starts with an initial size.
49     ///
50     /// # Errors
51     ///
52     /// Returns an error if `init` does not match the element type of the table,
53     /// or if `init` does not belong to the `store` provided.
54     ///
55     /// # Panics
56     ///
57     /// This function will panic when used with a [`Store`](`crate::Store`)
58     /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
59     /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`).
60     /// When using an async resource limiter, use [`Table::new_async`]
61     /// instead.
62     ///
63     /// # Examples
64     ///
65     /// ```
66     /// # use wasmtime::*;
67     /// # fn main() -> anyhow::Result<()> {
68     /// let engine = Engine::default();
69     /// let mut store = Store::new(&engine, ());
70     ///
71     /// let ty = TableType::new(RefType::FUNCREF, 2, None);
72     /// let table = Table::new(&mut store, ty, Ref::Func(None))?;
73     ///
74     /// let module = Module::new(
75     ///     &engine,
76     ///     "(module
77     ///         (table (import \"\" \"\") 2 funcref)
78     ///         (func $f (result i32)
79     ///             i32.const 10)
80     ///         (elem (i32.const 0) $f)
81     ///     )"
82     /// )?;
83     ///
84     /// let instance = Instance::new(&mut store, &module, &[table.into()])?;
85     /// // ...
86     /// # Ok(())
87     /// # }
88     /// ```
89     pub fn new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table> {
90         Table::_new(store.as_context_mut().0, ty, init)
91     }
92 
93     /// Async variant of [`Table::new`]. You must use this variant with
94     /// [`Store`](`crate::Store`)s which have a
95     /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
96     ///
97     /// # Panics
98     ///
99     /// This function will panic when used with a non-async
100     /// [`Store`](`crate::Store`)
101     #[cfg(feature = "async")]
102     pub async fn new_async(
103         mut store: impl AsContextMut<Data: Send>,
104         ty: TableType,
105         init: Ref,
106     ) -> Result<Table> {
107         let mut store = store.as_context_mut();
108         assert!(
109             store.0.async_support(),
110             "cannot use `new_async` without enabling async support on the config"
111         );
112         store
113             .on_fiber(|store| Table::_new(store.0, ty, init))
114             .await?
115     }
116 
117     fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result<Table> {
118         let table = generate_table_export(store, &ty)?;
119         let init = init.into_table_element(store, ty.element())?;
120         let (wasmtime_table, gc_store) = table.wasmtime_table(store, iter::empty());
121         wasmtime_table.fill(gc_store, 0, init, ty.minimum())?;
122         Ok(table)
123     }
124 
125     /// Returns the underlying type of this table, including its element type as
126     /// well as the maximum/minimum lower bounds.
127     ///
128     /// # Panics
129     ///
130     /// Panics if `store` does not own this table.
131     pub fn ty(&self, store: impl AsContext) -> TableType {
132         self._ty(store.as_context().0)
133     }
134 
135     fn _ty(&self, store: &StoreOpaque) -> TableType {
136         TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
137     }
138 
139     /// Returns the `runtime::Table` within `store` as well as the optional
140     /// `GcStore` in use within `store`.
141     ///
142     /// # Panics
143     ///
144     /// Panics if this table does not belong to `store`.
145     fn wasmtime_table<'a>(
146         &self,
147         store: &'a mut StoreOpaque,
148         lazy_init_range: impl Iterator<Item = u64>,
149     ) -> (&'a mut runtime::Table, Option<&'a mut GcStore>) {
150         self.instance.assert_belongs_to(store.id());
151         let (store, instance) = store.optional_gc_store_and_instance_mut(self.instance.instance());
152 
153         (
154             instance.get_defined_table_with_lazy_init(self.index, lazy_init_range),
155             store,
156         )
157     }
158 
159     /// Returns the table element value at `index`.
160     ///
161     /// Returns `None` if `index` is out of bounds.
162     ///
163     /// # Panics
164     ///
165     /// Panics if `store` does not own this table.
166     pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
167         let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
168         let (table, gc_store) = self.wasmtime_table(&mut store, iter::once(index));
169         match table.get(gc_store, index)? {
170             runtime::TableElement::FuncRef(f) => {
171                 // SAFETY: the `table` belongs to `store`, so elements within
172                 // the table must also belong to the store.
173                 let func = unsafe { f.map(|f| Func::from_vm_func_ref(store.id(), f)) };
174                 Some(func.into())
175             }
176 
177             runtime::TableElement::UninitFunc => {
178                 unreachable!("lazy init above should have converted UninitFunc")
179             }
180 
181             runtime::TableElement::GcRef(None) => {
182                 Some(Ref::null(self._ty(&store).element().heap_type()))
183             }
184 
185             #[cfg_attr(
186                 not(feature = "gc"),
187                 expect(unreachable_code, unused_variables, reason = "definitions cfg'd off")
188             )]
189             runtime::TableElement::GcRef(Some(x)) => {
190                 match self._ty(&store).element().heap_type().top() {
191                     HeapType::Any => {
192                         let x = AnyRef::from_cloned_gc_ref(&mut store, x);
193                         Some(x.into())
194                     }
195                     HeapType::Extern => {
196                         let x = ExternRef::from_cloned_gc_ref(&mut store, x);
197                         Some(x.into())
198                     }
199                     HeapType::Func => {
200                         unreachable!("never have TableElement::GcRef for func tables")
201                     }
202                     ty => unreachable!("not a top type: {ty:?}"),
203                 }
204             }
205 
206             runtime::TableElement::ContRef(_c) => {
207                 // TODO(#10248) Required to support stack switching in the embedder API.
208                 unimplemented!()
209             }
210         }
211     }
212 
213     /// Writes the `val` provided into `index` within this table.
214     ///
215     /// # Errors
216     ///
217     /// Returns an error if `index` is out of bounds, if `val` does not have
218     /// the right type to be stored in this table, or if `val` belongs to a
219     /// different store.
220     ///
221     /// # Panics
222     ///
223     /// Panics if `store` does not own this table.
224     pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
225         let store = store.as_context_mut().0;
226         let ty = self.ty(&store);
227         let val = val.into_table_element(store, ty.element())?;
228         let (table, _) = self.wasmtime_table(store, iter::empty());
229         table
230             .set(index, val)
231             .map_err(|()| anyhow!("table element index out of bounds"))
232     }
233 
234     /// Returns the current size of this table.
235     ///
236     /// # Panics
237     ///
238     /// Panics if `store` does not own this table.
239     pub fn size(&self, store: impl AsContext) -> u64 {
240         self.internal_size(store.as_context().0)
241     }
242 
243     pub(crate) fn internal_size(&self, store: &StoreOpaque) -> u64 {
244         // unwrap here should be ok because the runtime should always guarantee
245         // that we can fit the number of elements in a 64-bit integer.
246         u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
247     }
248 
249     /// Grows the size of this table by `delta` more elements, initialization
250     /// all new elements to `init`.
251     ///
252     /// Returns the previous size of this table if successful.
253     ///
254     /// # Errors
255     ///
256     /// Returns an error if the table cannot be grown by `delta`, for example
257     /// if it would cause the table to exceed its maximum size. Also returns an
258     /// error if `init` is not of the right type or if `init` does not belong to
259     /// `store`.
260     ///
261     /// # Panics
262     ///
263     /// Panics if `store` does not own this table.
264     ///
265     /// This function will panic when used with a [`Store`](`crate::Store`)
266     /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`)
267     /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`)).
268     /// When using an async resource limiter, use [`Table::grow_async`]
269     /// instead.
270     pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
271         let store = store.as_context_mut().0;
272         let ty = self.ty(&store);
273         let init = init.into_table_element(store, ty.element())?;
274         let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
275         // FIXME(#11179) shouldn't need to subvert the borrow checker
276         let table: *mut _ = table;
277         unsafe {
278             match (*table).grow(delta, init, store)? {
279                 Some(size) => {
280                     let vm = (*table).vmtable();
281                     store[self.instance].table_ptr(self.index).write(vm);
282                     // unwrap here should be ok because the runtime should always guarantee
283                     // that we can fit the table size in a 64-bit integer.
284                     Ok(u64::try_from(size).unwrap())
285                 }
286                 None => bail!("failed to grow table by `{}`", delta),
287             }
288         }
289     }
290 
291     /// Async variant of [`Table::grow`]. Required when using a
292     /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
293     ///
294     /// # Panics
295     ///
296     /// This function will panic when used with a non-async
297     /// [`Store`](`crate::Store`).
298     #[cfg(feature = "async")]
299     pub async fn grow_async(
300         &self,
301         mut store: impl AsContextMut<Data: Send>,
302         delta: u64,
303         init: Ref,
304     ) -> Result<u64> {
305         let mut store = store.as_context_mut();
306         assert!(
307             store.0.async_support(),
308             "cannot use `grow_async` without enabling async support on the config"
309         );
310         store
311             .on_fiber(|store| self.grow(store, delta, init))
312             .await?
313     }
314 
315     /// Copy `len` elements from `src_table[src_index..]` into
316     /// `dst_table[dst_index..]`.
317     ///
318     /// # Errors
319     ///
320     /// Returns an error if the range is out of bounds of either the source or
321     /// destination tables, or if the source table's element type does not match
322     /// the destination table's element type.
323     ///
324     /// # Panics
325     ///
326     /// Panics if `store` does not own either `dst_table` or `src_table`.
327     pub fn copy(
328         mut store: impl AsContextMut,
329         dst_table: &Table,
330         dst_index: u64,
331         src_table: &Table,
332         src_index: u64,
333         len: u64,
334     ) -> Result<()> {
335         let store = store.as_context_mut().0;
336 
337         let dst_ty = dst_table.ty(&store);
338         let src_ty = src_table.ty(&store);
339         src_ty
340             .element()
341             .ensure_matches(store.engine(), dst_ty.element())
342             .context(
343                 "type mismatch: source table's element type does not match \
344                  destination table's element type",
345             )?;
346 
347         let (dst_table, _) = dst_table.wasmtime_table(store, iter::empty());
348         // FIXME(#11179) shouldn't need to subvert the borrow checker
349         let dst_table: *mut _ = dst_table;
350         let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
351         let (src_table, _) = src_table.wasmtime_table(store, src_range);
352         // FIXME(#11179) shouldn't need to subvert the borrow checker
353         let src_table: *mut _ = src_table;
354         unsafe {
355             runtime::Table::copy(
356                 store.optional_gc_store_mut(),
357                 dst_table,
358                 src_table,
359                 dst_index,
360                 src_index,
361                 len,
362             )?;
363         }
364         Ok(())
365     }
366 
367     /// Fill `table[dst..(dst + len)]` with the given value.
368     ///
369     /// # Errors
370     ///
371     /// Returns an error if
372     ///
373     /// * `val` is not of the same type as this table's
374     ///   element type,
375     ///
376     /// * the region to be filled is out of bounds, or
377     ///
378     /// * `val` comes from a different `Store` from this table.
379     ///
380     /// # Panics
381     ///
382     /// Panics if `store` does not own either `dst_table` or `src_table`.
383     pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
384         let store = store.as_context_mut().0;
385         let ty = self.ty(&store);
386         let val = val.into_table_element(store, ty.element())?;
387 
388         let (table, gc_store) = self.wasmtime_table(store, iter::empty());
389         table.fill(gc_store, dst, val, len)?;
390 
391         Ok(())
392     }
393 
394     #[cfg(feature = "gc")]
395     pub(crate) fn trace_roots(
396         &self,
397         store: &mut StoreOpaque,
398         gc_roots_list: &mut crate::runtime::vm::GcRootsList,
399     ) {
400         if !self
401             ._ty(store)
402             .element()
403             .is_vmgcref_type_and_points_to_object()
404         {
405             return;
406         }
407 
408         let (table, _) = self.wasmtime_table(store, iter::empty());
409         for gc_ref in table.gc_refs_mut() {
410             if let Some(gc_ref) = gc_ref {
411                 unsafe {
412                     gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
413                 }
414             }
415         }
416     }
417 
418     pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
419         Table { instance, index }
420     }
421 
422     pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
423         let module = store[self.instance].env_module();
424         let index = module.table_index(self.index);
425         &module.tables[index]
426     }
427 
428     pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMTableImport {
429         let instance = &store[self.instance];
430         crate::runtime::vm::VMTableImport {
431             from: instance.table_ptr(self.index).into(),
432             vmctx: instance.vmctx().into(),
433             index: self.index,
434         }
435     }
436 
437     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
438         store.id() == self.instance.store_id()
439     }
440 
441     /// Get a stable hash key for this table.
442     ///
443     /// Even if the same underlying table definition is added to the
444     /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
445     /// this hash key will be consistent across all of these tables.
446     #[cfg_attr(
447         not(test),
448         expect(dead_code, reason = "Not used yet, but added for consistency")
449     )]
450     pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
451         store[self.instance].table_ptr(self.index).as_ptr().addr()
452     }
453 }
454 
455 #[cfg(test)]
456 mod tests {
457     use super::*;
458     use crate::{Instance, Module, Store};
459 
460     #[test]
461     fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
462         let mut store = Store::<()>::default();
463         let module = Module::new(
464             store.engine(),
465             r#"
466                 (module
467                     (table (export "t") 1 1 externref)
468                 )
469             "#,
470         )?;
471         let instance = Instance::new(&mut store, &module, &[])?;
472 
473         // Each time we `get_table`, we call `Table::from_wasmtime` which adds
474         // a new entry to `StoreData`, so `t1` and `t2` will have different
475         // indices into `StoreData`.
476         let t1 = instance.get_table(&mut store, "t").unwrap();
477         let t2 = instance.get_table(&mut store, "t").unwrap();
478 
479         // That said, they really point to the same table.
480         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
481         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
482         let e = ExternRef::new(&mut store, 42)?;
483         t1.set(&mut store, 0, e.into())?;
484         assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
485         assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
486 
487         // And therefore their hash keys are the same.
488         assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
489 
490         // But the hash keys are different from different tables.
491         let instance2 = Instance::new(&mut store, &module, &[])?;
492         let t3 = instance2.get_table(&mut store, "t").unwrap();
493         assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
494 
495         Ok(())
496     }
497 }
498