dusk_wasmtime/runtime/externals/
table.rs1use std::ptr::NonNull;
2
3use crate::store::{AutoAssertNoGc, StoreData, StoreOpaque, Stored};
4use crate::trampoline::generate_table_export;
5use crate::{AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, Ref, TableType};
6use anyhow::{anyhow, bail, Context, Result};
7use runtime::{GcRootsList, SendSyncPtr};
8use wasmtime_environ::TypeTrace;
9use wasmtime_runtime::{self as runtime};
10
11#[derive(Copy, Clone, Debug)]
25#[repr(transparent)] pub struct Table(pub(super) Stored<wasmtime_runtime::ExportTable>);
27
28impl Table {
29 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 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
81 #[cfg(feature = "async")]
90 pub async fn new_async<T>(
91 mut store: impl AsContextMut<Data = T>,
92 ty: TableType,
93 init: Ref,
94 ) -> Result<Table>
95 where
96 T: Send,
97 {
98 let mut store = store.as_context_mut();
99 assert!(
100 store.0.async_support(),
101 "cannot use `new_async` without enabling async support on the config"
102 );
103 store
104 .on_fiber(|store| Table::_new(store.0, ty, init))
105 .await?
106 }
107
108 fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result<Table> {
109 let wasmtime_export = generate_table_export(store, &ty)?;
110 let init = init.into_table_element(store, ty.element())?;
111 unsafe {
112 let table = Table::from_wasmtime_table(wasmtime_export, store);
113 let wasmtime_table = table.wasmtime_table(store, std::iter::empty());
114 (*wasmtime_table).fill(store.gc_store_mut()?, 0, init, ty.minimum())?;
115 Ok(table)
116 }
117 }
118
119 pub fn ty(&self, store: impl AsContext) -> TableType {
126 self._ty(store.as_context().0)
127 }
128
129 fn _ty(&self, store: &StoreOpaque) -> TableType {
130 let ty = &store[self.0].table.table;
131 TableType::from_wasmtime_table(store.engine(), ty)
132 }
133
134 fn wasmtime_table(
135 &self,
136 store: &mut StoreOpaque,
137 lazy_init_range: impl Iterator<Item = u32>,
138 ) -> *mut runtime::Table {
139 unsafe {
140 let export = &store[self.0];
141 wasmtime_runtime::Instance::from_vmctx(export.vmctx, |handle| {
142 let idx = handle.table_index(&*export.definition);
143 handle.get_defined_table_with_lazy_init(idx, lazy_init_range)
144 })
145 }
146 }
147
148 pub fn get(&self, mut store: impl AsContextMut, index: u32) -> Option<Ref> {
156 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
157 let table = self.wasmtime_table(&mut store, std::iter::once(index));
158 unsafe {
159 match (*table).get(store.unwrap_gc_store_mut(), index)? {
160 runtime::TableElement::FuncRef(f) => {
161 let func = Func::from_vm_func_ref(&mut store, f);
162 Some(func.into())
163 }
164
165 runtime::TableElement::UninitFunc => {
166 unreachable!("lazy init above should have converted UninitFunc")
167 }
168
169 runtime::TableElement::GcRef(None) => {
170 match self._ty(&store).element().heap_type().top(store.engine()) {
171 HeapType::Any => Some(Ref::Any(None)),
172 HeapType::Extern => Some(Ref::Extern(None)),
173 HeapType::Func => {
174 unreachable!("never have TableElement::GcRef for func tables")
175 }
176 ty => unreachable!("not a top type: {ty:?}"),
177 }
178 }
179
180 #[cfg_attr(not(feature = "gc"), allow(unreachable_code, unused_variables))]
181 runtime::TableElement::GcRef(Some(x)) => {
182 match self._ty(&store).element().heap_type().top(store.engine()) {
183 HeapType::Any => {
184 let x = AnyRef::from_cloned_gc_ref(&mut store, x);
185 Some(x.into())
186 }
187 HeapType::Extern => {
188 let x = ExternRef::from_cloned_gc_ref(&mut store, x);
189 Some(x.into())
190 }
191 HeapType::Func => {
192 unreachable!("never have TableElement::GcRef for func tables")
193 }
194 ty => unreachable!("not a top type: {ty:?}"),
195 }
196 }
197 }
198 }
199 }
200
201 pub fn set(&self, mut store: impl AsContextMut, index: u32, val: Ref) -> Result<()> {
213 let store = store.as_context_mut().0;
214 let ty = self.ty(&store);
215 let val = val.into_table_element(store, ty.element())?;
216 let table = self.wasmtime_table(store, std::iter::empty());
217 unsafe {
218 (*table)
219 .set(index, val)
220 .map_err(|()| anyhow!("table element index out of bounds"))
221 }
222 }
223
224 pub fn size(&self, store: impl AsContext) -> u32 {
230 self.internal_size(store.as_context().0)
231 }
232
233 pub(crate) fn internal_size(&self, store: &StoreOpaque) -> u32 {
234 unsafe { (*store[self.0].definition).current_elements }
235 }
236
237 pub fn grow(&self, mut store: impl AsContextMut, delta: u32, init: Ref) -> Result<u32> {
259 let store = store.as_context_mut().0;
260 let ty = self.ty(&store);
261 let init = init.into_table_element(store, ty.element())?;
262 let table = self.wasmtime_table(store, std::iter::empty());
263 unsafe {
264 match (*table).grow(delta, init, store)? {
265 Some(size) => {
266 let vm = (*table).vmtable();
267 *store[self.0].definition = vm;
268 Ok(size)
269 }
270 None => bail!("failed to grow table by `{}`", delta),
271 }
272 }
273 }
274
275 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
276 #[cfg(feature = "async")]
284 pub async fn grow_async<T>(
285 &self,
286 mut store: impl AsContextMut<Data = T>,
287 delta: u32,
288 init: Ref,
289 ) -> Result<u32>
290 where
291 T: Send,
292 {
293 let mut store = store.as_context_mut();
294 assert!(
295 store.0.async_support(),
296 "cannot use `grow_async` without enabling async support on the config"
297 );
298 store
299 .on_fiber(|store| self.grow(store, delta, init))
300 .await?
301 }
302
303 pub fn copy(
316 mut store: impl AsContextMut,
317 dst_table: &Table,
318 dst_index: u32,
319 src_table: &Table,
320 src_index: u32,
321 len: u32,
322 ) -> Result<()> {
323 let store = store.as_context_mut().0;
324
325 let dst_ty = dst_table.ty(&store);
326 let src_ty = src_table.ty(&store);
327 src_ty
328 .element()
329 .ensure_matches(store.engine(), dst_ty.element())
330 .context(
331 "type mismatch: source table's element type does not match \
332 destination table's element type",
333 )?;
334
335 let dst_table = dst_table.wasmtime_table(store, std::iter::empty());
336 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u32::MAX));
337 let src_table = src_table.wasmtime_table(store, src_range);
338 unsafe {
339 runtime::Table::copy(
340 store.gc_store_mut()?,
341 dst_table,
342 src_table,
343 dst_index,
344 src_index,
345 len,
346 )?;
347 }
348 Ok(())
349 }
350
351 pub fn fill(&self, mut store: impl AsContextMut, dst: u32, val: Ref, len: u32) -> Result<()> {
368 let store = store.as_context_mut().0;
369 let ty = self.ty(&store);
370 let val = val.into_table_element(store, ty.element())?;
371
372 let table = self.wasmtime_table(store, std::iter::empty());
373 unsafe {
374 (*table).fill(store.gc_store_mut()?, dst, val, len)?;
375 }
376
377 Ok(())
378 }
379
380 pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut GcRootsList) {
381 if !self._ty(store).element().is_gc_heap_type() {
382 return;
383 }
384
385 let table = self.wasmtime_table(store, std::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);
392 }
393 }
394 }
395 }
396
397 pub(crate) unsafe fn from_wasmtime_table(
398 mut wasmtime_export: wasmtime_runtime::ExportTable,
399 store: &mut StoreOpaque,
400 ) -> Table {
401 wasmtime_export
403 .table
404 .table
405 .wasm_ty
406 .canonicalize(&mut |module_index| {
407 wasmtime_runtime::Instance::from_vmctx(wasmtime_export.vmctx, |instance| {
408 instance.engine_type_index(module_index).bits()
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) -> wasmtime_runtime::VMTableImport {
420 let export = &store[self.0];
421 wasmtime_runtime::VMTableImport {
422 from: export.definition,
423 vmctx: export.vmctx,
424 }
425 }
426
427 #[allow(dead_code)] pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl std::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 let t1 = instance.get_table(&mut store, "t").unwrap();
460 let t2 = instance.get_table(&mut store, "t").unwrap();
461
462 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 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
472
473 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}