wasmtime/runtime/externals/table.rs
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/// A WebAssembly `table`, or an array of values.
17///
18/// Like [`Memory`][crate::Memory] a table is an indexed array of values, but
19/// unlike [`Memory`][crate::Memory] it's an array of WebAssembly reference type
20/// values rather than bytes. One of the most common usages of a table is a
21/// function table for wasm modules (a `funcref` table), where each element has
22/// the `ValType::FuncRef` type.
23///
24/// A [`Table`] "belongs" to the store that it was originally created within
25/// (either via [`Table::new`] or via instantiating a
26/// [`Module`](crate::Module)). Operations on a [`Table`] only work with the
27/// store it belongs to, and if another store is passed in by accident then
28/// methods will panic.
29#[derive(Copy, Clone, Debug)]
30#[repr(C)] // here for the C API
31pub struct Table {
32 instance: StoreInstanceId,
33 index: DefinedTableIndex,
34}
35
36// Double-check that the C representation in `extern.h` matches our in-Rust
37// representation here in terms of size/alignment/etc.
38const _: () = {
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 /// Creates a new [`Table`] with the given parameters.
50 ///
51 /// * `store` - the owner of the resulting [`Table`]
52 /// * `ty` - the type of this table, containing both the element type as
53 /// well as the initial size and maximum size, if any.
54 /// * `init` - the initial value to fill all table entries with, if the
55 /// table starts with an initial size.
56 ///
57 /// # Errors
58 ///
59 /// Returns an error if `init` does not match the element type of the table,
60 /// or if `init` does not belong to the `store` provided.
61 ///
62 /// This function will also return an error when used with a
63 /// [`Store`](`crate::Store`) which has a
64 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) (see also:
65 /// [`Store::limiter_async`](`crate::Store::limiter_async`). When using an
66 /// async resource limiter, use [`Table::new_async`] instead.
67 ///
68 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
69 /// memory allocation fails. See the `OutOfMemory` type's documentation for
70 /// details on Wasmtime's out-of-memory handling.
71 ///
72 /// # Examples
73 ///
74 /// ```
75 /// # use wasmtime::*;
76 /// # fn main() -> Result<()> {
77 /// let engine = Engine::default();
78 /// let mut store = Store::new(&engine, ());
79 ///
80 /// let ty = TableType::new(RefType::FUNCREF, 2, None);
81 /// let table = Table::new(&mut store, ty, Ref::Func(None))?;
82 ///
83 /// let module = Module::new(
84 /// &engine,
85 /// "(module
86 /// (table (import \"\" \"\") 2 funcref)
87 /// (func $f (result i32)
88 /// i32.const 10)
89 /// (elem (i32.const 0) $f)
90 /// )"
91 /// )?;
92 ///
93 /// let instance = Instance::new(&mut store, &module, &[table.into()])?;
94 /// // ...
95 /// # Ok(())
96 /// # }
97 /// ```
98 pub fn new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table> {
99 let (mut limiter, store) = store
100 .as_context_mut()
101 .0
102 .validate_sync_resource_limiter_and_store_opaque()?;
103 vm::assert_ready(Table::_new(store, limiter.as_mut(), ty, init))
104 }
105
106 /// Async variant of [`Table::new`].
107 ///
108 /// You must use this variant with [`Store`](`crate::Store`)s which have a
109 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
110 ///
111 /// # Errors
112 ///
113 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
114 /// memory allocation fails. See the `OutOfMemory` type's documentation for
115 /// details on Wasmtime's out-of-memory handling.
116 #[cfg(feature = "async")]
117 pub async fn new_async(
118 mut store: impl AsContextMut,
119 ty: TableType,
120 init: Ref,
121 ) -> Result<Table> {
122 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque();
123 Table::_new(store, limiter.as_mut(), ty, init).await
124 }
125
126 async fn _new(
127 store: &mut StoreOpaque,
128 limiter: Option<&mut StoreResourceLimiter<'_>>,
129 ty: TableType,
130 init: Ref,
131 ) -> Result<Table> {
132 let table = generate_table_export(store, limiter, &ty).await?;
133 table._fill(store, 0, init, ty.minimum())?;
134 Ok(table)
135 }
136
137 /// Returns the underlying type of this table, including its element type as
138 /// well as the maximum/minimum lower bounds.
139 ///
140 /// # Panics
141 ///
142 /// Panics if `store` does not own this table.
143 pub fn ty(&self, store: impl AsContext) -> TableType {
144 self.ty_(store.as_context().0)
145 }
146
147 pub(crate) fn ty_(&self, store: &StoreOpaque) -> TableType {
148 TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
149 }
150
151 /// Returns the `vm::Table` within `store` as well as the optional
152 /// `GcStore` in use within `store`.
153 ///
154 /// # Panics
155 ///
156 /// Panics if this table does not belong to `store`.
157 fn wasmtime_table<'a>(
158 &self,
159 store: &'a mut StoreOpaque,
160 lazy_init_range: impl IntoIterator<Item = u64>,
161 ) -> (&'a mut vm::Table, Option<&'a mut GcStore>) {
162 self.instance.assert_belongs_to(store.id());
163 let (store, registry, instance) =
164 store.optional_gc_store_and_registry_and_instance_mut(self.instance.instance());
165
166 (
167 instance.get_defined_table_with_lazy_init(registry, self.index, lazy_init_range),
168 store,
169 )
170 }
171
172 /// Returns the table element value at `index`.
173 ///
174 /// Returns `None` if `index` is out of bounds.
175 ///
176 /// # Panics
177 ///
178 /// Panics if `store` does not own this table.
179 pub fn get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref> {
180 let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
181 let (table, _gc_store) = self.wasmtime_table(&mut store, [index]);
182 match table.element_type() {
183 TableElementType::Func => {
184 let ptr = table.get_func(index).ok()?;
185 Some(
186 // SAFETY: `store` owns this table, so therefore it owns all
187 // functions within the table too.
188 ptr.map(|p| unsafe { Func::from_vm_func_ref(store.id(), p) })
189 .into(),
190 )
191 }
192 TableElementType::GcRef => {
193 let gc_ref = table
194 .get_gc_ref(index)
195 .ok()?
196 .map(|r| r.unchecked_copy())
197 .map(|r| store.clone_gc_ref(&r));
198 Some(match self.ty_(&store).element().heap_type().top() {
199 HeapType::Extern => {
200 Ref::Extern(gc_ref.map(|r| ExternRef::from_cloned_gc_ref(&mut store, r)))
201 }
202 HeapType::Any => {
203 Ref::Any(gc_ref.map(|r| AnyRef::from_cloned_gc_ref(&mut store, r)))
204 }
205 HeapType::Exn => {
206 Ref::Exn(gc_ref.map(|r| ExnRef::from_cloned_gc_ref(&mut store, r)))
207 }
208 _ => unreachable!(),
209 })
210 }
211 // TODO(#10248) Required to support stack switching in the embedder
212 // API.
213 TableElementType::Cont => panic!("unimplemented table for cont"),
214 }
215 }
216
217 /// Writes the `val` provided into `index` within this table.
218 ///
219 /// # Errors
220 ///
221 /// Returns an error if `index` is out of bounds, if `val` does not have
222 /// the right type to be stored in this table, or if `val` belongs to a
223 /// different store.
224 ///
225 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
226 /// memory allocation fails. See the `OutOfMemory` type's documentation for
227 /// details on Wasmtime's out-of-memory handling.
228 ///
229 /// # Panics
230 ///
231 /// Panics if `store` does not own this table.
232 pub fn set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()> {
233 self.set_(store.as_context_mut().0, index, val)
234 }
235
236 pub(crate) fn set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()> {
237 let ty = self.ty_(store);
238 match element_type(&ty) {
239 TableElementType::Func => {
240 let element = val.into_table_func(store, ty.element())?;
241 let (table, _gc_store) = self.wasmtime_table(store, iter::empty());
242 table.set_func(index, element)?;
243 }
244 TableElementType::GcRef => {
245 let mut store = AutoAssertNoGc::new(store);
246 let element = val.into_table_gc_ref(&mut store, ty.element())?;
247 // Note that `unchecked_copy` should be ok as we're under an
248 // `AutoAssertNoGc` which means that despite this not being
249 // rooted we don't have to worry about it going away.
250 let element = element.map(|r| r.unchecked_copy());
251 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
252 table.set_gc_ref(gc_store, index, element.as_ref())?;
253 }
254 // TODO(#10248) Required to support stack switching in the embedder
255 // API.
256 TableElementType::Cont => bail!("unimplemented table for cont"),
257 }
258 Ok(())
259 }
260
261 /// Returns the current size of this table.
262 ///
263 /// # Panics
264 ///
265 /// Panics if `store` does not own this table.
266 pub fn size(&self, store: impl AsContext) -> u64 {
267 self.size_(store.as_context().0)
268 }
269
270 pub(crate) fn size_(&self, store: &StoreOpaque) -> u64 {
271 // unwrap here should be ok because the runtime should always guarantee
272 // that we can fit the number of elements in a 64-bit integer.
273 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
274 }
275
276 /// Grows the size of this table by `delta` more elements, initialization
277 /// all new elements to `init`.
278 ///
279 /// Returns the previous size of this table if successful.
280 ///
281 /// # Errors
282 ///
283 /// Returns an error if the table cannot be grown by `delta`, for example
284 /// if it would cause the table to exceed its maximum size. Also returns an
285 /// error if `init` is not of the right type or if `init` does not belong to
286 /// `store`.
287 ///
288 /// This function also returns an error when used with a
289 /// [`Store`](`crate::Store`) which has a
290 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) (see also:
291 /// [`Store::limiter_async`](`crate::Store::limiter_async`)). When using an
292 /// async resource limiter, use [`Table::grow_async`] instead.
293 ///
294 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
295 /// memory allocation fails. See the `OutOfMemory` type's documentation for
296 /// details on Wasmtime's out-of-memory handling.
297 ///
298 /// # Panics
299 ///
300 /// Panics if `store` does not own this table.
301 pub fn grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64> {
302 let store = store.as_context_mut();
303 store.0.validate_sync_resource_limiter_and_store_opaque()?;
304 vm::assert_ready(self._grow(store, delta, init))
305 }
306
307 async fn _grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64> {
308 let store = store.0;
309 let ty = self.ty(&store);
310 let (mut limiter, store) = store.resource_limiter_and_store_opaque();
311 let limiter = limiter.as_mut();
312 let result = match element_type(&ty) {
313 TableElementType::Func => {
314 let element = init
315 .into_table_func(store, ty.element())?
316 .map(SendSyncPtr::new);
317 self.instance
318 .get_mut(store)
319 .defined_table_grow(self.index, async |table| {
320 // SAFETY: in the context of `defined_table_grow` this
321 // is safe to call as it'll update the internal table
322 // pointer in the instance.
323 unsafe { table.grow_func(limiter, delta, element).await }
324 })
325 .await?
326 }
327 TableElementType::GcRef => {
328 let mut store = AutoAssertNoGc::new(store);
329 let element = init
330 .into_table_gc_ref(&mut store, ty.element())?
331 .map(|r| r.unchecked_copy());
332 let (gc_store, instance) = self.instance.get_with_gc_store_mut(&mut store);
333 instance
334 .defined_table_grow(self.index, async |table| {
335 // SAFETY: in the context of `defined_table_grow` this
336 // is safe to call as it'll update the internal table
337 // pointer in the instance.
338 unsafe {
339 table
340 .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
341 .await
342 }
343 })
344 .await?
345 }
346 // TODO(#10248) Required to support stack switching in the
347 // embedder API.
348 TableElementType::Cont => bail!("unimplemented table for cont"),
349 };
350 match result {
351 Some(size) => {
352 // unwrap here should be ok because the runtime should always
353 // guarantee that we can fit the table size in a 64-bit integer.
354 Ok(u64::try_from(size).unwrap())
355 }
356 None => bail!("failed to grow table by `{delta}`"),
357 }
358 }
359
360 /// Async variant of [`Table::grow`].
361 ///
362 /// Required when using a
363 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
364 ///
365 /// # Errors
366 ///
367 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
368 /// memory allocation fails. See the `OutOfMemory` type's documentation for
369 /// details on Wasmtime's out-of-memory handling.
370 ///
371 /// # Panics
372 ///
373 /// This function will panic when if the store doesn't own the table.
374 #[cfg(feature = "async")]
375 pub async fn grow_async(
376 &self,
377 mut store: impl AsContextMut,
378 delta: u64,
379 init: Ref,
380 ) -> Result<u64> {
381 self._grow(store.as_context_mut(), delta, init).await
382 }
383
384 /// Copy `len` elements from `src_table[src_index..]` into
385 /// `dst_table[dst_index..]`.
386 ///
387 /// # Errors
388 ///
389 /// Returns an error if the range is out of bounds of either the source or
390 /// destination tables, or if the source table's element type does not match
391 /// the destination table's element type.
392 ///
393 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
394 /// memory allocation fails. See the `OutOfMemory` type's documentation for
395 /// details on Wasmtime's out-of-memory handling.
396 ///
397 /// # Panics
398 ///
399 /// Panics if `store` does not own either `dst_table` or `src_table`.
400 pub fn copy(
401 mut store: impl AsContextMut,
402 dst_table: &Table,
403 dst_index: u64,
404 src_table: &Table,
405 src_index: u64,
406 len: u64,
407 ) -> Result<()> {
408 let store = store.as_context_mut().0;
409
410 let dst_ty = dst_table.ty(&store);
411 let src_ty = src_table.ty(&store);
412 src_ty
413 .element()
414 .ensure_matches(store.engine(), dst_ty.element())
415 .context(
416 "type mismatch: source table's element type does not match \
417 destination table's element type",
418 )?;
419
420 // SAFETY: the the two tables have the same type, as type-checked above.
421 unsafe {
422 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
423 }
424 Ok(())
425 }
426
427 /// Copies the elements of `src_table` to `dst_table`.
428 ///
429 /// # Panics
430 ///
431 /// Panics if the either table doesn't belong to `store`.
432 ///
433 /// # Safety
434 ///
435 /// Requires that the two tables have previously been type-checked to have
436 /// the same type.
437 pub(crate) unsafe fn copy_raw(
438 store: &mut StoreOpaque,
439 dst_table: &Table,
440 dst_index: u64,
441 src_table: &Table,
442 src_index: u64,
443 len: u64,
444 ) -> Result<(), Trap> {
445 // Handle lazy initialization of the source table first before doing
446 // anything else.
447 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
448 src_table.wasmtime_table(store, src_range);
449
450 // validate `dst_table` belongs to `store`.
451 dst_table.wasmtime_table(store, iter::empty());
452
453 // Figure out which of the three cases we're in:
454 //
455 // 1. Cross-instance table copy.
456 // 2. Intra-instance table copy.
457 // 3. Intra-table copy.
458 //
459 // We handle each of them slightly differently.
460 let src_instance = src_table.instance.instance();
461 let dst_instance = dst_table.instance.instance();
462 match (
463 src_instance == dst_instance,
464 src_table.index == dst_table.index,
465 ) {
466 // 1. Cross-instance table copy: split the mutable store borrow into
467 // two mutable instance borrows, get each instance's defined table,
468 // and do the copy.
469 (false, _) => {
470 // SAFETY: accessing two instances mutably at the same time
471 // requires only accessing defined entities on each instance
472 // which is done below with `get_defined_*` methods.
473 let (gc_store, [src_instance, dst_instance]) = unsafe {
474 store.optional_gc_store_and_instances_mut([src_instance, dst_instance])
475 };
476 src_instance.get_defined_table(src_table.index).copy_to(
477 dst_instance.get_defined_table(dst_table.index),
478 gc_store,
479 dst_index,
480 src_index,
481 len,
482 )
483 }
484
485 // 2. Intra-instance, distinct-tables copy: split the mutable
486 // instance borrow into two distinct mutable table borrows and do
487 // the copy.
488 (true, false) => {
489 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
490 let [(_, src_table), (_, dst_table)] = instance
491 .tables_mut()
492 .get_disjoint_mut([src_table.index, dst_table.index])
493 .unwrap();
494 src_table.copy_to(dst_table, gc_store, dst_index, src_index, len)
495 }
496
497 // 3. Intra-table copy: get the table and copy within it!
498 (true, true) => {
499 let (gc_store, instance) = store.optional_gc_store_and_instance_mut(src_instance);
500 instance
501 .get_defined_table(src_table.index)
502 .copy_within(gc_store, dst_index, src_index, len)
503 }
504 }
505 }
506
507 /// Fill `table[dst..(dst + len)]` with the given value.
508 ///
509 /// # Errors
510 ///
511 /// Returns an error if
512 ///
513 /// * `val` is not of the same type as this table's
514 /// element type,
515 ///
516 /// * the region to be filled is out of bounds, or
517 ///
518 /// * `val` comes from a different `Store` from this table.
519 ///
520 /// This function will return an [`OutOfMemory`][crate::OutOfMemory] error when
521 /// memory allocation fails. See the `OutOfMemory` type's documentation for
522 /// details on Wasmtime's out-of-memory handling.
523 ///
524 /// # Panics
525 ///
526 /// Panics if `store` does not own either `dst_table` or `src_table`.
527 pub fn fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()> {
528 self._fill(store.as_context_mut().0, dst, val, len)
529 }
530
531 pub(crate) fn _fill(
532 &self,
533 store: &mut StoreOpaque,
534 dst: u64,
535 val: Ref,
536 len: u64,
537 ) -> Result<()> {
538 let ty = self.ty_(&store);
539 match element_type(&ty) {
540 TableElementType::Func => {
541 let val = val.into_table_func(store, ty.element())?;
542 let (table, _) = self.wasmtime_table(store, iter::empty());
543 table.fill_func(dst, val, len)?;
544 }
545 TableElementType::GcRef => {
546 // Note that `val` is a `VMGcRef` temporarily read from the
547 // store here, and blocking GC with `AutoAssertNoGc` should
548 // ensure that it's not collected while being worked on here.
549 let mut store = AutoAssertNoGc::new(store);
550 let val = val.into_table_gc_ref(&mut store, ty.element())?;
551 let val = val.map(|g| g.unchecked_copy());
552 let (table, gc_store) = self.wasmtime_table(&mut store, iter::empty());
553 table.fill_gc_ref(gc_store, dst, val.as_ref(), len)?;
554 }
555 // TODO(#10248) Required to support stack switching in the embedder
556 // API.
557 TableElementType::Cont => bail!("unimplemented table for cont"),
558 }
559
560 Ok(())
561 }
562
563 #[cfg(feature = "gc")]
564 pub(crate) fn trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList) {
565 if !self
566 .ty_(store)
567 .element()
568 .is_vmgcref_type_and_points_to_object()
569 {
570 return;
571 }
572
573 let (table, _) = self.wasmtime_table(store, iter::empty());
574 for gc_ref in table.gc_refs_mut() {
575 if let Some(gc_ref) = gc_ref {
576 unsafe {
577 gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm table element");
578 }
579 }
580 }
581 }
582
583 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
584 Table { instance, index }
585 }
586
587 pub(crate) fn wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table {
588 let module = store[self.instance].env_module();
589 let index = module.table_index(self.index);
590 &module.tables[index]
591 }
592
593 pub(crate) fn vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport {
594 let instance = &store[self.instance];
595 vm::VMTableImport {
596 from: instance.table_ptr(self.index).into(),
597 vmctx: instance.vmctx().into(),
598 index: self.index,
599 }
600 }
601
602 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
603 store.id() == self.instance.store_id()
604 }
605
606 /// Returns a stable identifier for this table within its store.
607 ///
608 /// This allows distinguishing tables when introspecting them
609 /// e.g. via debug APIs.
610 #[cfg(feature = "debug")]
611 pub fn debug_index_in_store(&self) -> u64 {
612 u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
613 }
614
615 /// Get a stable hash key for this table.
616 ///
617 /// Even if the same underlying table definition is added to the
618 /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
619 /// this hash key will be consistent across all of these tables.
620 #[cfg_attr(
621 not(test),
622 expect(dead_code, reason = "Not used yet, but added for consistency")
623 )]
624 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
625 store[self.instance].table_ptr(self.index).as_ptr().addr()
626 }
627}
628
629fn element_type(ty: &TableType) -> TableElementType {
630 match ty.element().heap_type().top() {
631 HeapType::Func => TableElementType::Func,
632 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
633 HeapType::Cont => TableElementType::Cont,
634 _ => unreachable!(),
635 }
636}
637
638impl Ref {
639 fn into_table_func(
640 self,
641 store: &mut StoreOpaque,
642 ty: &RefType,
643 ) -> Result<Option<NonNull<VMFuncRef>>> {
644 self.ensure_matches_ty(store, &ty)
645 .context("type mismatch: value does not match table element type")?;
646
647 match (self, ty.heap_type().top()) {
648 (Ref::Func(None), HeapType::Func) => {
649 assert!(ty.is_nullable());
650 Ok(None)
651 }
652 (Ref::Func(Some(f)), HeapType::Func) => {
653 debug_assert!(
654 f.comes_from_same_store(store),
655 "checked in `ensure_matches_ty`"
656 );
657 Ok(Some(f.vm_func_ref(store)))
658 }
659
660 _ => unreachable!("checked that the value matches the type above"),
661 }
662 }
663
664 fn into_table_gc_ref<'a>(
665 self,
666 store: &'a mut AutoAssertNoGc<'_>,
667 ty: &RefType,
668 ) -> Result<Option<&'a VMGcRef>> {
669 self.ensure_matches_ty(store, &ty)
670 .context("type mismatch: value does not match table element type")?;
671
672 match (self, ty.heap_type().top()) {
673 (Ref::Extern(e), HeapType::Extern) => match e {
674 None => {
675 assert!(ty.is_nullable());
676 Ok(None)
677 }
678 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
679 },
680
681 (Ref::Any(a), HeapType::Any) => match a {
682 None => {
683 assert!(ty.is_nullable());
684 Ok(None)
685 }
686 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
687 },
688
689 (Ref::Exn(e), HeapType::Exn) => match e {
690 None => {
691 assert!(ty.is_nullable());
692 Ok(None)
693 }
694 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
695 },
696
697 _ => unreachable!("checked that the value matches the type above"),
698 }
699 }
700}
701
702#[cfg(test)]
703mod tests {
704 use super::*;
705 use crate::{Instance, Module, Store};
706
707 #[test]
708 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
709 let mut store = Store::<()>::default();
710 let module = Module::new(
711 store.engine(),
712 r#"
713 (module
714 (table (export "t") 1 1 externref)
715 )
716 "#,
717 )?;
718 let instance = Instance::new(&mut store, &module, &[])?;
719
720 // Each time we `get_table`, we call `Table::from_wasmtime` which adds
721 // a new entry to `StoreData`, so `t1` and `t2` will have different
722 // indices into `StoreData`.
723 let t1 = instance.get_table(&mut store, "t").unwrap();
724 let t2 = instance.get_table(&mut store, "t").unwrap();
725
726 // That said, they really point to the same table.
727 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
728 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
729 let e = ExternRef::new(&mut store, 42)?;
730 t1.set(&mut store, 0, e.into())?;
731 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
732 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
733
734 // And therefore their hash keys are the same.
735 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
736
737 // But the hash keys are different from different tables.
738 let instance2 = Instance::new(&mut store, &module, &[])?;
739 let t3 = instance2.get_table(&mut store, "t").unwrap();
740 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
741
742 Ok(())
743 }
744
745 #[test]
746 fn grow_is_send() {
747 fn _assert_send<T: Send>(_: T) {}
748 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
749 _assert_send(table.grow(store, 0, init))
750 }
751 }
752}