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