wasmtime/runtime/externals/
table.rs1use crate::prelude::*;
2use crate::runtime::vm::{self as runtime};
3use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque};
4use crate::trampoline::generate_table_export;
5use crate::{AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, Ref, TableType};
6use core::iter;
7use wasmtime_environ::{DefinedTableIndex, TypeTrace};
8
9#[derive(Copy, Clone, Debug)]
23#[repr(C)] pub struct Table {
25 instance: StoreInstanceId,
26 index: DefinedTableIndex,
27}
28
29const _: () = {
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
41impl Table {
42 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 #[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 wasmtime_export = generate_table_export(store, &ty)?;
119 let init = init.into_table_element(store, ty.element())?;
120 unsafe {
121 let table = Table::from_wasmtime_table(wasmtime_export, store);
122 let wasmtime_table = table.wasmtime_table(store, iter::empty());
123 (*wasmtime_table).fill(store.optional_gc_store_mut(), 0, init, ty.minimum())?;
124 Ok(table)
125 }
126 }
127
128 pub fn ty(&self, store: impl AsContext) -> TableType {
135 self._ty(store.as_context().0)
136 }
137
138 fn _ty(&self, store: &StoreOpaque) -> TableType {
139 TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
140 }
141
142 fn wasmtime_table(
143 &self,
144 store: &mut StoreOpaque,
145 lazy_init_range: impl Iterator<Item = u64>,
146 ) -> *mut runtime::Table {
147 unsafe {
148 let instance = &store[self.instance];
149 let vmctx = instance.vmctx();
150 crate::runtime::vm::Instance::from_vmctx(vmctx, |handle| {
151 handle.get_defined_table_with_lazy_init(self.index, lazy_init_range)
152 })
153 }
154 }
155
156 pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
164 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
165 let table = self.wasmtime_table(&mut store, iter::once(index));
166 let gc_store = store.optional_gc_store_mut();
167 unsafe {
168 match (*table).get(gc_store, index)? {
169 runtime::TableElement::FuncRef(f) => {
170 let func = f.map(|f| Func::from_vm_func_ref(&store, f));
171 Some(func.into())
172 }
173
174 runtime::TableElement::UninitFunc => {
175 unreachable!("lazy init above should have converted UninitFunc")
176 }
177
178 runtime::TableElement::GcRef(None) => {
179 Some(Ref::null(self._ty(&store).element().heap_type()))
180 }
181
182 #[cfg_attr(not(feature = "gc"), allow(unreachable_code, unused_variables))]
183 runtime::TableElement::GcRef(Some(x)) => {
184 match self._ty(&store).element().heap_type().top() {
185 HeapType::Any => {
186 let x = AnyRef::from_cloned_gc_ref(&mut store, x);
187 Some(x.into())
188 }
189 HeapType::Extern => {
190 let x = ExternRef::from_cloned_gc_ref(&mut store, x);
191 Some(x.into())
192 }
193 HeapType::Func => {
194 unreachable!("never have TableElement::GcRef for func tables")
195 }
196 ty => unreachable!("not a top type: {ty:?}"),
197 }
198 }
199
200 runtime::TableElement::ContRef(_c) => {
201 unimplemented!()
203 }
204 }
205 }
206 }
207
208 pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
220 let store = store.as_context_mut().0;
221 let ty = self.ty(&store);
222 let val = val.into_table_element(store, ty.element())?;
223 let table = self.wasmtime_table(store, iter::empty());
224 unsafe {
225 (*table)
226 .set(index, val)
227 .map_err(|()| anyhow!("table element index out of bounds"))
228 }
229 }
230
231 pub fn size(&self, store: impl AsContext) -> u64 {
237 self.internal_size(store.as_context().0)
238 }
239
240 pub(crate) fn internal_size(&self, store: &StoreOpaque) -> u64 {
241 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
244 }
245
246 pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
268 let store = store.as_context_mut().0;
269 let ty = self.ty(&store);
270 let init = init.into_table_element(store, ty.element())?;
271 let table = self.wasmtime_table(store, iter::empty());
272 unsafe {
273 match (*table).grow(delta, init, store)? {
274 Some(size) => {
275 let vm = (*table).vmtable();
276 store[self.instance].table_ptr(self.index).write(vm);
277 Ok(u64::try_from(size).unwrap())
280 }
281 None => bail!("failed to grow table by `{}`", delta),
282 }
283 }
284 }
285
286 #[cfg(feature = "async")]
294 pub async fn grow_async(
295 &self,
296 mut store: impl AsContextMut<Data: Send>,
297 delta: u64,
298 init: Ref,
299 ) -> Result<u64> {
300 let mut store = store.as_context_mut();
301 assert!(
302 store.0.async_support(),
303 "cannot use `grow_async` without enabling async support on the config"
304 );
305 store
306 .on_fiber(|store| self.grow(store, delta, init))
307 .await?
308 }
309
310 pub fn copy(
323 mut store: impl AsContextMut,
324 dst_table: &Table,
325 dst_index: u64,
326 src_table: &Table,
327 src_index: u64,
328 len: u64,
329 ) -> Result<()> {
330 let store = store.as_context_mut().0;
331
332 let dst_ty = dst_table.ty(&store);
333 let src_ty = src_table.ty(&store);
334 src_ty
335 .element()
336 .ensure_matches(store.engine(), dst_ty.element())
337 .context(
338 "type mismatch: source table's element type does not match \
339 destination table's element type",
340 )?;
341
342 let dst_table = dst_table.wasmtime_table(store, iter::empty());
343 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
344 let src_table = src_table.wasmtime_table(store, src_range);
345 unsafe {
346 runtime::Table::copy(
347 store.optional_gc_store_mut(),
348 dst_table,
349 src_table,
350 dst_index,
351 src_index,
352 len,
353 )?;
354 }
355 Ok(())
356 }
357
358 pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
375 let store = store.as_context_mut().0;
376 let ty = self.ty(&store);
377 let val = val.into_table_element(store, ty.element())?;
378
379 let table = self.wasmtime_table(store, iter::empty());
380 unsafe {
381 (*table).fill(store.optional_gc_store_mut(), dst, val, len)?;
382 }
383
384 Ok(())
385 }
386
387 #[cfg(feature = "gc")]
388 pub(crate) fn trace_roots(
389 &self,
390 store: &mut StoreOpaque,
391 gc_roots_list: &mut crate::runtime::vm::GcRootsList,
392 ) {
393 if !self
394 ._ty(store)
395 .element()
396 .is_vmgcref_type_and_points_to_object()
397 {
398 return;
399 }
400
401 let table = self.wasmtime_table(store, iter::empty());
402 for gc_ref in unsafe { (*table).gc_refs_mut() } {
403 if let Some(gc_ref) = gc_ref {
404 unsafe {
405 gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
406 }
407 }
408 }
409 }
410
411 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
412 Table { instance, index }
413 }
414
415 pub(crate) unsafe fn from_wasmtime_table(
416 wasmtime_export: crate::runtime::vm::ExportTable,
417 store: &StoreOpaque,
418 ) -> Table {
419 debug_assert!(
420 wasmtime_export
421 .table
422 .ref_type
423 .is_canonicalized_for_runtime_usage()
424 );
425 Table {
426 instance: store.vmctx_id(wasmtime_export.vmctx),
427 index: wasmtime_export.index,
428 }
429 }
430
431 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
432 let module = store[self.instance].env_module();
433 let index = module.table_index(self.index);
434 &module.tables[index]
435 }
436
437 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMTableImport {
438 let instance = &store[self.instance];
439 crate::runtime::vm::VMTableImport {
440 from: instance.table_ptr(self.index).into(),
441 vmctx: instance.vmctx().into(),
442 index: self.index,
443 }
444 }
445
446 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
447 store.id() == self.instance.store_id()
448 }
449
450 #[allow(dead_code)] pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
457 store[self.instance].table_ptr(self.index).as_ptr().addr()
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::{Instance, Module, Store};
465
466 #[test]
467 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
468 let mut store = Store::<()>::default();
469 let module = Module::new(
470 store.engine(),
471 r#"
472 (module
473 (table (export "t") 1 1 externref)
474 )
475 "#,
476 )?;
477 let instance = Instance::new(&mut store, &module, &[])?;
478
479 let t1 = instance.get_table(&mut store, "t").unwrap();
483 let t2 = instance.get_table(&mut store, "t").unwrap();
484
485 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
487 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
488 let e = ExternRef::new(&mut store, 42)?;
489 t1.set(&mut store, 0, e.into())?;
490 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
491 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
492
493 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
495
496 let instance2 = Instance::new(&mut store, &module, &[])?;
498 let t3 = instance2.get_table(&mut store, "t").unwrap();
499 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
500
501 Ok(())
502 }
503}