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