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, instance) = store.optional_gc_store_and_instance_mut(self.instance.instance());
158
159 (
160 instance.get_defined_table_with_lazy_init(self.index, lazy_init_range),
161 store,
162 )
163 }
164
165 pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
173 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
174 let (table, _gc_store) = self.wasmtime_table(&mut store, [index]);
175 match table.element_type() {
176 TableElementType::Func => {
177 let ptr = table.get_func(index).ok()?;
178 Some(
179 ptr.map(|p| unsafe { Func::from_vm_func_ref(store.id(), p) })
182 .into(),
183 )
184 }
185 TableElementType::GcRef => {
186 let gc_ref = table
187 .get_gc_ref(index)
188 .ok()?
189 .map(|r| r.unchecked_copy())
190 .map(|r| store.clone_gc_ref(&r));
191 Some(match self._ty(&store).element().heap_type().top() {
192 HeapType::Extern => {
193 Ref::Extern(gc_ref.map(|r| ExternRef::from_cloned_gc_ref(&mut store, r)))
194 }
195 HeapType::Any => {
196 Ref::Any(gc_ref.map(|r| AnyRef::from_cloned_gc_ref(&mut store, r)))
197 }
198 HeapType::Exn => {
199 Ref::Exn(gc_ref.map(|r| ExnRef::from_cloned_gc_ref(&mut store, r)))
200 }
201 _ => unreachable!(),
202 })
203 }
204 TableElementType::Cont => panic!("unimplemented table for cont"),
207 }
208 }
209
210 pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
222 self.set_(store.as_context_mut().0, index, val)
223 }
224
225 pub(crate) fn set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()> {
226 let ty = self._ty(store);
227 match element_type(&ty) {
228 TableElementType::Func => {
229 let element = val.into_table_func(store, ty.element())?;
230 let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
231 table.set_func(index, element)?;
232 }
233 TableElementType::GcRef => {
234 let mut store = AutoAssertNoGc::new(store);
235 let element = val.into_table_gc_ref(&mut store, ty.element())?;
236 let element = element.map(|r| r.unchecked_copy());
240 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
241 table.set_gc_ref(gc_store, index, element.as_ref())?;
242 }
243 TableElementType::Cont => bail!("unimplemented table for cont"),
246 }
247 Ok(())
248 }
249
250 pub fn size(&self, store: impl AsContext) -> u64 {
256 self._size(store.as_context().0)
257 }
258
259 pub(crate) fn _size(&self, store: &StoreOpaque) -> u64 {
260 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
263 }
264
265 pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
287 vm::one_poll(self._grow(store.as_context_mut(), delta, init))
288 .expect("must use `grow_async` when async resource limiters are in use")
289 }
290
291 async fn _grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64> {
292 let store = store.0;
293 let ty = self.ty(&store);
294 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
295 let limiter = limiter.as_mut();
296 let result = match element_type(&ty) {
297 TableElementType::Func => {
298 let element = init
299 .into_table_func(store, ty.element())?
300 .map(SendSyncPtr::new);
301 self.instance
302 .get_mut(store)
303 .defined_table_grow(self.index, async |table| {
304 unsafe { table.grow_func(limiter, delta, element).await }
308 })
309 .await?
310 }
311 TableElementType::GcRef => {
312 let mut store = AutoAssertNoGc::new(store);
313 let element = init
314 .into_table_gc_ref(&mut store, ty.element())?
315 .map(|r| r.unchecked_copy());
316 let (gc_store, instance) = self.instance.get_with_gc_store_mut(&mut store);
317 instance
318 .defined_table_grow(self.index, async |table| {
319 unsafe {
323 table
324 .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
325 .await
326 }
327 })
328 .await?
329 }
330 TableElementType::Cont => bail!("unimplemented table for cont"),
333 };
334 match result {
335 Some(size) => {
336 Ok(u64::try_from(size).unwrap())
339 }
340 None => bail!("failed to grow table by `{}`", delta),
341 }
342 }
343
344 #[cfg(feature = "async")]
352 pub async fn grow_async(
353 &self,
354 mut store: impl AsContextMut,
355 delta: u64,
356 init: Ref,
357 ) -> Result<u64> {
358 self._grow(store.as_context_mut(), delta, init).await
359 }
360
361 pub fn copy(
374 mut store: impl AsContextMut,
375 dst_table: &Table,
376 dst_index: u64,
377 src_table: &Table,
378 src_index: u64,
379 len: u64,
380 ) -> Result<()> {
381 let store = store.as_context_mut().0;
382
383 let dst_ty = dst_table.ty(&store);
384 let src_ty = src_table.ty(&store);
385 src_ty
386 .element()
387 .ensure_matches(store.engine(), dst_ty.element())
388 .context(
389 "type mismatch: source table's element type does not match \
390 destination table's element type",
391 )?;
392
393 unsafe {
395 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
396 }
397 Ok(())
398 }
399
400 pub(crate) unsafe fn copy_raw(
411 store: &mut StoreOpaque,
412 dst_table: &Table,
413 dst_index: u64,
414 src_table: &Table,
415 src_index: u64,
416 len: u64,
417 ) -> Result<(), Trap> {
418 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
421 src_table.wasmtime_table(store, src_range);
422
423 dst_table.wasmtime_table(store, iter::empty());
425
426 let src_instance = src_table.instance.instance();
434 let dst_instance = dst_table.instance.instance();
435 match (
436 src_instance == dst_instance,
437 src_table.index == dst_table.index,
438 ) {
439 (false, _) => {
443 let (gc_store, [src_instance, dst_instance]) = unsafe {
447 store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
448 };
449 src_instance.get_defined_table(src_table.index).copy_to(
450 dst_instance.get_defined_table(dst_table.index),
451 gc_store,
452 dst_index,
453 src_index,
454 len,
455 )
456 }
457
458 (true, false) => {
462 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
463 let [(_, src_table), (_, dst_table)] = instance
464 .tables_mut()
465 .get_disjoint_mut([src_table.index, dst_table.index])
466 .unwrap();
467 src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
468 }
469
470 (true, true) => {
472 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
473 instance
474 .get_defined_table(src_table.index)
475 .copy_within(gc_store, dst_index, src_index, len)
476 }
477 }
478 }
479
480 pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
497 self._fill(store.as_context_mut().0, dst, val, len)
498 }
499
500 pub(crate) fn _fill(
501 &self,
502 store: &mut StoreOpaque,
503 dst: u64,
504 val: Ref,
505 len: u64,
506 ) -> Result<()> {
507 let ty = self._ty(&store);
508 match element_type(&ty) {
509 TableElementType::Func => {
510 let val = val.into_table_func(store, ty.element())?;
511 let (table, _) = self.wasmtime_table(store, iter::empty());
512 table.fill_func(dst, val, len)?;
513 }
514 TableElementType::GcRef => {
515 let mut store = AutoAssertNoGc::new(store);
519 let val = val.into_table_gc_ref(&mut store, ty.element())?;
520 let val = val.map(|g| g.unchecked_copy());
521 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
522 table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
523 }
524 TableElementType::Cont => bail!("unimplemented table for cont"),
527 }
528
529 Ok(())
530 }
531
532 #[cfg(feature = "gc")]
533 pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
534 if !self
535 ._ty(store)
536 .element()
537 .is_vmgcref_type_and_points_to_object()
538 {
539 return;
540 }
541
542 let (table, _) = self.wasmtime_table(store, iter::empty());
543 for gc_ref in table.gc_refs_mut() {
544 if let Some(gc_ref) = gc_ref {
545 unsafe {
546 gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
547 }
548 }
549 }
550 }
551
552 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
553 Table { instance, index }
554 }
555
556 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
557 let module = store[self.instance].env_module();
558 let index = module.table_index(self.index);
559 &module.tables[index]
560 }
561
562 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport {
563 let instance = &store[self.instance];
564 vm::VMTableImport {
565 from: instance.table_ptr(self.index).into(),
566 vmctx: instance.vmctx().into(),
567 index: self.index,
568 }
569 }
570
571 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
572 store.id() == self.instance.store_id()
573 }
574
575 #[cfg_attr(
581 not(test),
582 expect(dead_code, reason = "Not used yet, but added for consistency")
583 )]
584 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
585 store[self.instance].table_ptr(self.index).as_ptr().addr()
586 }
587}
588
589fn element_type(ty: &TableType) -> TableElementType {
590 match ty.element().heap_type().top() {
591 HeapType::Func => TableElementType::Func,
592 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
593 HeapType::Cont => TableElementType::Cont,
594 _ => unreachable!(),
595 }
596}
597
598impl Ref {
599 fn into_table_func(
600 self,
601 store: &mut StoreOpaque,
602 ty: &RefType,
603 ) -> Result<Option<NonNull<VMFuncRef>>> {
604 self.ensure_matches_ty(store, &ty)
605 .context("type mismatch: value does not match table element type")?;
606
607 match (self, ty.heap_type().top()) {
608 (Ref::Func(None), HeapType::Func) => {
609 assert!(ty.is_nullable());
610 Ok(None)
611 }
612 (Ref::Func(Some(f)), HeapType::Func) => {
613 debug_assert!(
614 f.comes_from_same_store(store),
615 "checked in `ensure_matches_ty`"
616 );
617 Ok(Some(f.vm_func_ref(store)))
618 }
619
620 _ => unreachable!("checked that the value matches the type above"),
621 }
622 }
623
624 fn into_table_gc_ref<'a>(
625 self,
626 store: &'a mut AutoAssertNoGc<'_>,
627 ty: &RefType,
628 ) -> Result<Option<&'a VMGcRef>> {
629 self.ensure_matches_ty(store, &ty)
630 .context("type mismatch: value does not match table element type")?;
631
632 match (self, ty.heap_type().top()) {
633 (Ref::Extern(e), HeapType::Extern) => match e {
634 None => {
635 assert!(ty.is_nullable());
636 Ok(None)
637 }
638 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
639 },
640
641 (Ref::Any(a), HeapType::Any) => match a {
642 None => {
643 assert!(ty.is_nullable());
644 Ok(None)
645 }
646 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
647 },
648
649 (Ref::Exn(e), HeapType::Exn) => match e {
650 None => {
651 assert!(ty.is_nullable());
652 Ok(None)
653 }
654 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
655 },
656
657 _ => unreachable!("checked that the value matches the type above"),
658 }
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665 use crate::{Instance, Module, Store};
666
667 #[test]
668 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
669 let mut store = Store::<()>::default();
670 let module = Module::new(
671 store.engine(),
672 r#"
673 (module
674 (table (export "t") 1 1 externref)
675 )
676 "#,
677 )?;
678 let instance = Instance::new(&mut store, &module, &[])?;
679
680 let t1 = instance.get_table(&mut store, "t").unwrap();
684 let t2 = instance.get_table(&mut store, "t").unwrap();
685
686 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
688 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
689 let e = ExternRef::new(&mut store, 42)?;
690 t1.set(&mut store, 0, e.into())?;
691 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
692 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
693
694 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
696
697 let instance2 = Instance::new(&mut store, &module, &[])?;
699 let t3 = instance2.get_table(&mut store, "t").unwrap();
700 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
701
702 Ok(())
703 }
704
705 #[test]
706 fn grow_is_send() {
707 fn _assert_send<T: Send>(_: T) {}
708 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
709 _assert_send(table.grow(store, 0, init))
710 }
711 }
712}