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> {
95 let (mut limiter, store) = store
96 .as_context_mut()
97 .0
98 .validate_sync_resource_limiter_and_store_opaque()?;
99 vm::assert_ready(Table::_new(store, limiter.as_mut(), ty, init))
100 }
101
102 #[cfg(feature = "async")]
107 pub async fn new_async(
108 mut store: impl AsContextMut,
109 ty: TableType,
110 init: Ref,
111 ) -> Result<Table> {
112 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
113 Table::_new(store, limiter.as_mut(), ty, init).await
114 }
115
116 async fn _new(
117 store: &mut StoreOpaque,
118 limiter: Option<&mut StoreResourceLimiter<'_>>,
119 ty: TableType,
120 init: Ref,
121 ) -> Result<Table> {
122 let table = generate_table_export(store, limiter, &ty).await?;
123 table._fill(store, 0, init, ty.minimum())?;
124 Ok(table)
125 }
126
127 pub fn ty(&self, store: impl AsContext) -> TableType {
134 self._ty(store.as_context().0)
135 }
136
137 fn _ty(&self, store: &StoreOpaque) -> TableType {
138 TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
139 }
140
141 fn wasmtime_table<'a>(
148 &self,
149 store: &'a mut StoreOpaque,
150 lazy_init_range: impl IntoIterator<Item = u64>,
151 ) -> (&'a mut vm::Table, Option<&'a mut GcStore>) {
152 self.instance.assert_belongs_to(store.id());
153 let (store, registry, instance) =
154 store.optional_gc_store_and_registry_and_instance_mut(self.instance.instance());
155
156 (
157 instance.get_defined_table_with_lazy_init(registry, self.index, lazy_init_range),
158 store,
159 )
160 }
161
162 pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
170 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
171 let (table, _gc_store) = self.wasmtime_table(&mut store, [index]);
172 match table.element_type() {
173 TableElementType::Func => {
174 let ptr = table.get_func(index).ok()?;
175 Some(
176 ptr.map(|p| unsafe { Func::from_vm_func_ref(store.id(), p) })
179 .into(),
180 )
181 }
182 TableElementType::GcRef => {
183 let gc_ref = table
184 .get_gc_ref(index)
185 .ok()?
186 .map(|r| r.unchecked_copy())
187 .map(|r| store.clone_gc_ref(&r));
188 Some(match self._ty(&store).element().heap_type().top() {
189 HeapType::Extern => {
190 Ref::Extern(gc_ref.map(|r| ExternRef::from_cloned_gc_ref(&mut store, r)))
191 }
192 HeapType::Any => {
193 Ref::Any(gc_ref.map(|r| AnyRef::from_cloned_gc_ref(&mut store, r)))
194 }
195 HeapType::Exn => {
196 Ref::Exn(gc_ref.map(|r| ExnRef::from_cloned_gc_ref(&mut store, r)))
197 }
198 _ => unreachable!(),
199 })
200 }
201 TableElementType::Cont => panic!("unimplemented table for cont"),
204 }
205 }
206
207 pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
219 self.set_(store.as_context_mut().0, index, val)
220 }
221
222 pub(crate) fn set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()> {
223 let ty = self._ty(store);
224 match element_type(&ty) {
225 TableElementType::Func => {
226 let element = val.into_table_func(store, ty.element())?;
227 let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
228 table.set_func(index, element)?;
229 }
230 TableElementType::GcRef => {
231 let mut store = AutoAssertNoGc::new(store);
232 let element = val.into_table_gc_ref(&mut store, ty.element())?;
233 let element = element.map(|r| r.unchecked_copy());
237 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
238 table.set_gc_ref(gc_store, index, element.as_ref())?;
239 }
240 TableElementType::Cont => bail!("unimplemented table for cont"),
243 }
244 Ok(())
245 }
246
247 pub fn size(&self, store: impl AsContext) -> u64 {
253 self._size(store.as_context().0)
254 }
255
256 pub(crate) fn _size(&self, store: &StoreOpaque) -> u64 {
257 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
260 }
261
262 pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
284 let store = store.as_context_mut();
285 store.0.validate_sync_resource_limiter_and_store_opaque()?;
286 vm::assert_ready(self._grow(store, delta, init))
287 }
288
289 async fn _grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64> {
290 let store = store.0;
291 let ty = self.ty(&store);
292 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
293 let limiter = limiter.as_mut();
294 let result = match element_type(&ty) {
295 TableElementType::Func => {
296 let element = init
297 .into_table_func(store, ty.element())?
298 .map(SendSyncPtr::new);
299 self.instance
300 .get_mut(store)
301 .defined_table_grow(self.index, async |table| {
302 unsafe { table.grow_func(limiter, delta, element).await }
306 })
307 .await?
308 }
309 TableElementType::GcRef => {
310 let mut store = AutoAssertNoGc::new(store);
311 let element = init
312 .into_table_gc_ref(&mut store, ty.element())?
313 .map(|r| r.unchecked_copy());
314 let (gc_store, instance) = self.instance.get_with_gc_store_mut(&mut store);
315 instance
316 .defined_table_grow(self.index, async |table| {
317 unsafe {
321 table
322 .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
323 .await
324 }
325 })
326 .await?
327 }
328 TableElementType::Cont => bail!("unimplemented table for cont"),
331 };
332 match result {
333 Some(size) => {
334 Ok(u64::try_from(size).unwrap())
337 }
338 None => bail!("failed to grow table by `{delta}`"),
339 }
340 }
341
342 #[cfg(feature = "async")]
351 pub async fn grow_async(
352 &self,
353 mut store: impl AsContextMut,
354 delta: u64,
355 init: Ref,
356 ) -> Result<u64> {
357 self._grow(store.as_context_mut(), delta, init).await
358 }
359
360 pub fn copy(
373 mut store: impl AsContextMut,
374 dst_table: &Table,
375 dst_index: u64,
376 src_table: &Table,
377 src_index: u64,
378 len: u64,
379 ) -> Result<()> {
380 let store = store.as_context_mut().0;
381
382 let dst_ty = dst_table.ty(&store);
383 let src_ty = src_table.ty(&store);
384 src_ty
385 .element()
386 .ensure_matches(store.engine(), dst_ty.element())
387 .context(
388 "type mismatch: source table's element type does not match \
389 destination table's element type",
390 )?;
391
392 unsafe {
394 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
395 }
396 Ok(())
397 }
398
399 pub(crate) unsafe fn copy_raw(
410 store: &mut StoreOpaque,
411 dst_table: &Table,
412 dst_index: u64,
413 src_table: &Table,
414 src_index: u64,
415 len: u64,
416 ) -> Result<(), Trap> {
417 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
420 src_table.wasmtime_table(store, src_range);
421
422 dst_table.wasmtime_table(store, iter::empty());
424
425 let src_instance = src_table.instance.instance();
433 let dst_instance = dst_table.instance.instance();
434 match (
435 src_instance == dst_instance,
436 src_table.index == dst_table.index,
437 ) {
438 (false, _) => {
442 let (gc_store, [src_instance, dst_instance]) = unsafe {
446 store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
447 };
448 src_instance.get_defined_table(src_table.index).copy_to(
449 dst_instance.get_defined_table(dst_table.index),
450 gc_store,
451 dst_index,
452 src_index,
453 len,
454 )
455 }
456
457 (true, false) => {
461 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
462 let [(_, src_table), (_, dst_table)] = instance
463 .tables_mut()
464 .get_disjoint_mut([src_table.index, dst_table.index])
465 .unwrap();
466 src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
467 }
468
469 (true, true) => {
471 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
472 instance
473 .get_defined_table(src_table.index)
474 .copy_within(gc_store, dst_index, src_index, len)
475 }
476 }
477 }
478
479 pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
496 self._fill(store.as_context_mut().0, dst, val, len)
497 }
498
499 pub(crate) fn _fill(
500 &self,
501 store: &mut StoreOpaque,
502 dst: u64,
503 val: Ref,
504 len: u64,
505 ) -> Result<()> {
506 let ty = self._ty(&store);
507 match element_type(&ty) {
508 TableElementType::Func => {
509 let val = val.into_table_func(store, ty.element())?;
510 let (table, _) = self.wasmtime_table(store, iter::empty());
511 table.fill_func(dst, val, len)?;
512 }
513 TableElementType::GcRef => {
514 let mut store = AutoAssertNoGc::new(store);
518 let val = val.into_table_gc_ref(&mut store, ty.element())?;
519 let val = val.map(|g| g.unchecked_copy());
520 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
521 table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
522 }
523 TableElementType::Cont => bail!("unimplemented table for cont"),
526 }
527
528 Ok(())
529 }
530
531 #[cfg(feature = "gc")]
532 pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
533 if !self
534 ._ty(store)
535 .element()
536 .is_vmgcref_type_and_points_to_object()
537 {
538 return;
539 }
540
541 let (table, _) = self.wasmtime_table(store, iter::empty());
542 for gc_ref in table.gc_refs_mut() {
543 if let Some(gc_ref) = gc_ref {
544 unsafe {
545 gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
546 }
547 }
548 }
549 }
550
551 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
552 Table { instance, index }
553 }
554
555 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
556 let module = store[self.instance].env_module();
557 let index = module.table_index(self.index);
558 &module.tables[index]
559 }
560
561 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport {
562 let instance = &store[self.instance];
563 vm::VMTableImport {
564 from: instance.table_ptr(self.index).into(),
565 vmctx: instance.vmctx().into(),
566 index: self.index,
567 }
568 }
569
570 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
571 store.id() == self.instance.store_id()
572 }
573
574 #[cfg_attr(
580 not(test),
581 expect(dead_code, reason = "Not used yet, but added for consistency")
582 )]
583 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
584 store[self.instance].table_ptr(self.index).as_ptr().addr()
585 }
586}
587
588fn element_type(ty: &TableType) -> TableElementType {
589 match ty.element().heap_type().top() {
590 HeapType::Func => TableElementType::Func,
591 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
592 HeapType::Cont => TableElementType::Cont,
593 _ => unreachable!(),
594 }
595}
596
597impl Ref {
598 fn into_table_func(
599 self,
600 store: &mut StoreOpaque,
601 ty: &RefType,
602 ) -> Result<Option<NonNull<VMFuncRef>>> {
603 self.ensure_matches_ty(store, &ty)
604 .context("type mismatch: value does not match table element type")?;
605
606 match (self, ty.heap_type().top()) {
607 (Ref::Func(None), HeapType::Func) => {
608 assert!(ty.is_nullable());
609 Ok(None)
610 }
611 (Ref::Func(Some(f)), HeapType::Func) => {
612 debug_assert!(
613 f.comes_from_same_store(store),
614 "checked in `ensure_matches_ty`"
615 );
616 Ok(Some(f.vm_func_ref(store)))
617 }
618
619 _ => unreachable!("checked that the value matches the type above"),
620 }
621 }
622
623 fn into_table_gc_ref<'a>(
624 self,
625 store: &'a mut AutoAssertNoGc<'_>,
626 ty: &RefType,
627 ) -> Result<Option<&'a VMGcRef>> {
628 self.ensure_matches_ty(store, &ty)
629 .context("type mismatch: value does not match table element type")?;
630
631 match (self, ty.heap_type().top()) {
632 (Ref::Extern(e), HeapType::Extern) => match e {
633 None => {
634 assert!(ty.is_nullable());
635 Ok(None)
636 }
637 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
638 },
639
640 (Ref::Any(a), HeapType::Any) => match a {
641 None => {
642 assert!(ty.is_nullable());
643 Ok(None)
644 }
645 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
646 },
647
648 (Ref::Exn(e), HeapType::Exn) => match e {
649 None => {
650 assert!(ty.is_nullable());
651 Ok(None)
652 }
653 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
654 },
655
656 _ => unreachable!("checked that the value matches the type above"),
657 }
658 }
659}
660
661#[cfg(test)]
662mod tests {
663 use super::*;
664 use crate::{Instance, Module, Store};
665
666 #[test]
667 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
668 let mut store = Store::<()>::default();
669 let module = Module::new(
670 store.engine(),
671 r#"
672 (module
673 (table (export "t") 1 1 externref)
674 )
675 "#,
676 )?;
677 let instance = Instance::new(&mut store, &module, &[])?;
678
679 let t1 = instance.get_table(&mut store, "t").unwrap();
683 let t2 = instance.get_table(&mut store, "t").unwrap();
684
685 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
687 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
688 let e = ExternRef::new(&mut store, 42)?;
689 t1.set(&mut store, 0, e.into())?;
690 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
691 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
692
693 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
695
696 let instance2 = Instance::new(&mut store, &module, &[])?;
698 let t3 = instance2.get_table(&mut store, "t").unwrap();
699 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
700
701 Ok(())
702 }
703
704 #[test]
705 fn grow_is_send() {
706 fn _assert_send<T: Send>(_: T) {}
707 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
708 _assert_send(table.grow(store, 0, init))
709 }
710 }
711}