1use crate::prelude::*;
2use crate::runtime::RootedGcRefImpl;
3use crate::runtime::vm::{
4 self, GcStore, SendSyncPtr, TableElementType, VMFuncRef, VMGcRef, VMStore,
5};
6use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque, StoreResourceLimiter};
7use crate::trampoline::generate_table_export;
8use crate::{
9 AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, HeapType, Ref, RefType,
10 StoreContextMut, TableType, Trap,
11};
12use core::iter;
13use core::ptr::NonNull;
14use wasmtime_environ::DefinedTableIndex;
15
16#[derive(Copy, Clone, Debug)]
30#[repr(C)] pub struct Table {
32 instance: StoreInstanceId,
33 index: DefinedTableIndex,
34}
35
36const _: () = {
39 #[repr(C)]
40 struct Tmp(u64, u32);
41 #[repr(C)]
42 struct C(Tmp, u32);
43 assert!(core::mem::size_of::<C>() == core::mem::size_of::<Table>());
44 assert!(core::mem::align_of::<C>() == core::mem::align_of::<Table>());
45 assert!(core::mem::offset_of!(Table, instance) == 0);
46};
47
48impl Table {
49 pub fn new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table> {
97 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
98 vm::one_poll(Table::_new(store, limiter.as_mut(), ty, init))
99 .expect("must use `new_async` when async resource limiters are in use")
100 }
101
102 #[cfg(feature = "async")]
111 pub async fn new_async(
112 mut store: impl AsContextMut,
113 ty: TableType,
114 init: Ref,
115 ) -> Result<Table> {
116 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
117 Table::_new(store, limiter.as_mut(), ty, init).await
118 }
119
120 async fn _new(
121 store: &mut StoreOpaque,
122 limiter: Option<&mut StoreResourceLimiter<'_>>,
123 ty: TableType,
124 init: Ref,
125 ) -> Result<Table> {
126 let table = generate_table_export(store, limiter, &ty).await?;
127 table._fill(store, 0, init, ty.minimum())?;
128 Ok(table)
129 }
130
131 pub fn ty(&self, store: impl AsContext) -> TableType {
138 self._ty(store.as_context().0)
139 }
140
141 fn _ty(&self, store: &StoreOpaque) -> TableType {
142 TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
143 }
144
145 fn wasmtime_table<'a>(
152 &self,
153 store: &'a mut StoreOpaque,
154 lazy_init_range: impl IntoIterator<Item = u64>,
155 ) -> (&'a mut vm::Table, Option<&'a mut GcStore>) {
156 self.instance.assert_belongs_to(store.id());
157 let (store, registry, instance) =
158 store.optional_gc_store_and_registry_and_instance_mut(self.instance.instance());
159
160 (
161 instance.get_defined_table_with_lazy_init(registry, self.index, lazy_init_range),
162 store,
163 )
164 }
165
166 pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
174 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
175 let (table, _gc_store) = self.wasmtime_table(&mut store, [index]);
176 match table.element_type() {
177 TableElementType::Func => {
178 let ptr = table.get_func(index).ok()?;
179 Some(
180 ptr.map(|p| unsafe { Func::from_vm_func_ref(store.id(), p) })
183 .into(),
184 )
185 }
186 TableElementType::GcRef => {
187 let gc_ref = table
188 .get_gc_ref(index)
189 .ok()?
190 .map(|r| r.unchecked_copy())
191 .map(|r| store.clone_gc_ref(&r));
192 Some(match self._ty(&store).element().heap_type().top() {
193 HeapType::Extern => {
194 Ref::Extern(gc_ref.map(|r| ExternRef::from_cloned_gc_ref(&mut store, r)))
195 }
196 HeapType::Any => {
197 Ref::Any(gc_ref.map(|r| AnyRef::from_cloned_gc_ref(&mut store, r)))
198 }
199 HeapType::Exn => {
200 Ref::Exn(gc_ref.map(|r| ExnRef::from_cloned_gc_ref(&mut store, r)))
201 }
202 _ => unreachable!(),
203 })
204 }
205 TableElementType::Cont => panic!("unimplemented table for cont"),
208 }
209 }
210
211 pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
223 self.set_(store.as_context_mut().0, index, val)
224 }
225
226 pub(crate) fn set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()> {
227 let ty = self._ty(store);
228 match element_type(&ty) {
229 TableElementType::Func => {
230 let element = val.into_table_func(store, ty.element())?;
231 let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
232 table.set_func(index, element)?;
233 }
234 TableElementType::GcRef => {
235 let mut store = AutoAssertNoGc::new(store);
236 let element = val.into_table_gc_ref(&mut store, ty.element())?;
237 let element = element.map(|r| r.unchecked_copy());
241 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
242 table.set_gc_ref(gc_store, index, element.as_ref())?;
243 }
244 TableElementType::Cont => bail!("unimplemented table for cont"),
247 }
248 Ok(())
249 }
250
251 pub fn size(&self, store: impl AsContext) -> u64 {
257 self._size(store.as_context().0)
258 }
259
260 pub(crate) fn _size(&self, store: &StoreOpaque) -> u64 {
261 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
264 }
265
266 pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
288 vm::one_poll(self._grow(store.as_context_mut(), delta, init))
289 .expect("must use `grow_async` when async resource limiters are in use")
290 }
291
292 async fn _grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64> {
293 let store = store.0;
294 let ty = self.ty(&store);
295 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
296 let limiter = limiter.as_mut();
297 let result = match element_type(&ty) {
298 TableElementType::Func => {
299 let element = init
300 .into_table_func(store, ty.element())?
301 .map(SendSyncPtr::new);
302 self.instance
303 .get_mut(store)
304 .defined_table_grow(self.index, async |table| {
305 unsafe { table.grow_func(limiter, delta, element).await }
309 })
310 .await?
311 }
312 TableElementType::GcRef => {
313 let mut store = AutoAssertNoGc::new(store);
314 let element = init
315 .into_table_gc_ref(&mut store, ty.element())?
316 .map(|r| r.unchecked_copy());
317 let (gc_store, instance) = self.instance.get_with_gc_store_mut(&mut store);
318 instance
319 .defined_table_grow(self.index, async |table| {
320 unsafe {
324 table
325 .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
326 .await
327 }
328 })
329 .await?
330 }
331 TableElementType::Cont => bail!("unimplemented table for cont"),
334 };
335 match result {
336 Some(size) => {
337 Ok(u64::try_from(size).unwrap())
340 }
341 None => bail!("failed to grow table by `{delta}`"),
342 }
343 }
344
345 #[cfg(feature = "async")]
353 pub async fn grow_async(
354 &self,
355 mut store: impl AsContextMut,
356 delta: u64,
357 init: Ref,
358 ) -> Result<u64> {
359 self._grow(store.as_context_mut(), delta, init).await
360 }
361
362 pub fn copy(
375 mut store: impl AsContextMut,
376 dst_table: &Table,
377 dst_index: u64,
378 src_table: &Table,
379 src_index: u64,
380 len: u64,
381 ) -> Result<()> {
382 let store = store.as_context_mut().0;
383
384 let dst_ty = dst_table.ty(&store);
385 let src_ty = src_table.ty(&store);
386 src_ty
387 .element()
388 .ensure_matches(store.engine(), dst_ty.element())
389 .context(
390 "type mismatch: source table's element type does not match \
391 destination table's element type",
392 )?;
393
394 unsafe {
396 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
397 }
398 Ok(())
399 }
400
401 pub(crate) unsafe fn copy_raw(
412 store: &mut StoreOpaque,
413 dst_table: &Table,
414 dst_index: u64,
415 src_table: &Table,
416 src_index: u64,
417 len: u64,
418 ) -> Result<(), Trap> {
419 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
422 src_table.wasmtime_table(store, src_range);
423
424 dst_table.wasmtime_table(store, iter::empty());
426
427 let src_instance = src_table.instance.instance();
435 let dst_instance = dst_table.instance.instance();
436 match (
437 src_instance == dst_instance,
438 src_table.index == dst_table.index,
439 ) {
440 (false, _) => {
444 let (gc_store, [src_instance, dst_instance]) = unsafe {
448 store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
449 };
450 src_instance.get_defined_table(src_table.index).copy_to(
451 dst_instance.get_defined_table(dst_table.index),
452 gc_store,
453 dst_index,
454 src_index,
455 len,
456 )
457 }
458
459 (true, false) => {
463 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
464 let [(_, src_table), (_, dst_table)] = instance
465 .tables_mut()
466 .get_disjoint_mut([src_table.index, dst_table.index])
467 .unwrap();
468 src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
469 }
470
471 (true, true) => {
473 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
474 instance
475 .get_defined_table(src_table.index)
476 .copy_within(gc_store, dst_index, src_index, len)
477 }
478 }
479 }
480
481 pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
498 self._fill(store.as_context_mut().0, dst, val, len)
499 }
500
501 pub(crate) fn _fill(
502 &self,
503 store: &mut StoreOpaque,
504 dst: u64,
505 val: Ref,
506 len: u64,
507 ) -> Result<()> {
508 let ty = self._ty(&store);
509 match element_type(&ty) {
510 TableElementType::Func => {
511 let val = val.into_table_func(store, ty.element())?;
512 let (table, _) = self.wasmtime_table(store, iter::empty());
513 table.fill_func(dst, val, len)?;
514 }
515 TableElementType::GcRef => {
516 let mut store = AutoAssertNoGc::new(store);
520 let val = val.into_table_gc_ref(&mut store, ty.element())?;
521 let val = val.map(|g| g.unchecked_copy());
522 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
523 table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
524 }
525 TableElementType::Cont => bail!("unimplemented table for cont"),
528 }
529
530 Ok(())
531 }
532
533 #[cfg(feature = "gc")]
534 pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
535 if !self
536 ._ty(store)
537 .element()
538 .is_vmgcref_type_and_points_to_object()
539 {
540 return;
541 }
542
543 let (table, _) = self.wasmtime_table(store, iter::empty());
544 for gc_ref in table.gc_refs_mut() {
545 if let Some(gc_ref) = gc_ref {
546 unsafe {
547 gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
548 }
549 }
550 }
551 }
552
553 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
554 Table { instance, index }
555 }
556
557 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
558 let module = store[self.instance].env_module();
559 let index = module.table_index(self.index);
560 &module.tables[index]
561 }
562
563 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport {
564 let instance = &store[self.instance];
565 vm::VMTableImport {
566 from: instance.table_ptr(self.index).into(),
567 vmctx: instance.vmctx().into(),
568 index: self.index,
569 }
570 }
571
572 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
573 store.id() == self.instance.store_id()
574 }
575
576 #[cfg_attr(
582 not(test),
583 expect(dead_code, reason = "Not used yet, but added for consistency")
584 )]
585 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
586 store[self.instance].table_ptr(self.index).as_ptr().addr()
587 }
588}
589
590fn element_type(ty: &TableType) -> TableElementType {
591 match ty.element().heap_type().top() {
592 HeapType::Func => TableElementType::Func,
593 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
594 HeapType::Cont => TableElementType::Cont,
595 _ => unreachable!(),
596 }
597}
598
599impl Ref {
600 fn into_table_func(
601 self,
602 store: &mut StoreOpaque,
603 ty: &RefType,
604 ) -> Result<Option<NonNull<VMFuncRef>>> {
605 self.ensure_matches_ty(store, &ty)
606 .context("type mismatch: value does not match table element type")?;
607
608 match (self, ty.heap_type().top()) {
609 (Ref::Func(None), HeapType::Func) => {
610 assert!(ty.is_nullable());
611 Ok(None)
612 }
613 (Ref::Func(Some(f)), HeapType::Func) => {
614 debug_assert!(
615 f.comes_from_same_store(store),
616 "checked in `ensure_matches_ty`"
617 );
618 Ok(Some(f.vm_func_ref(store)))
619 }
620
621 _ => unreachable!("checked that the value matches the type above"),
622 }
623 }
624
625 fn into_table_gc_ref<'a>(
626 self,
627 store: &'a mut AutoAssertNoGc<'_>,
628 ty: &RefType,
629 ) -> Result<Option<&'a VMGcRef>> {
630 self.ensure_matches_ty(store, &ty)
631 .context("type mismatch: value does not match table element type")?;
632
633 match (self, ty.heap_type().top()) {
634 (Ref::Extern(e), HeapType::Extern) => match e {
635 None => {
636 assert!(ty.is_nullable());
637 Ok(None)
638 }
639 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
640 },
641
642 (Ref::Any(a), HeapType::Any) => match a {
643 None => {
644 assert!(ty.is_nullable());
645 Ok(None)
646 }
647 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
648 },
649
650 (Ref::Exn(e), HeapType::Exn) => match e {
651 None => {
652 assert!(ty.is_nullable());
653 Ok(None)
654 }
655 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
656 },
657
658 _ => unreachable!("checked that the value matches the type above"),
659 }
660 }
661}
662
663#[cfg(test)]
664mod tests {
665 use super::*;
666 use crate::{Instance, Module, Store};
667
668 #[test]
669 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
670 let mut store = Store::<()>::default();
671 let module = Module::new(
672 store.engine(),
673 r#"
674 (module
675 (table (export "t") 1 1 externref)
676 )
677 "#,
678 )?;
679 let instance = Instance::new(&mut store, &module, &[])?;
680
681 let t1 = instance.get_table(&mut store, "t").unwrap();
685 let t2 = instance.get_table(&mut store, "t").unwrap();
686
687 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
689 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
690 let e = ExternRef::new(&mut store, 42)?;
691 t1.set(&mut store, 0, e.into())?;
692 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
693 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
694
695 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
697
698 let instance2 = Instance::new(&mut store, &module, &[])?;
700 let t3 = instance2.get_table(&mut store, "t").unwrap();
701 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
702
703 Ok(())
704 }
705
706 #[test]
707 fn grow_is_send() {
708 fn _assert_send<T: Send>(_: T) {}
709 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
710 _assert_send(table.grow(store, 0, init))
711 }
712 }
713}