1 use crate::prelude::*;
2 use crate::runtime::RootedGcRefImpl;
3 use crate::runtime::vm::{
4 self, GcStore, SendSyncPtr, TableElementType, VMFuncRef, VMGcRef, VMStore,
5 };
6 use crate::store::{AutoAssertNoGc, StoreInstanceId, StoreOpaque, StoreResourceLimiter};
7 use crate::trampoline::generate_table_export;
8 use crate::{
9 AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, HeapType, Ref, RefType,
10 StoreContextMut, TableType, Trap,
11 };
12 use core::iter;
13 use core::ptr::NonNull;
14 use 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
31 pub 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.
38 const _: () = {
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
48 impl 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 /// # Examples
69 ///
70 /// ```
71 /// # use wasmtime::*;
72 /// # fn main() -> Result<()> {
73 /// let engine = Engine::default();
74 /// let mut store = Store::new(&engine, ());
75 ///
76 /// let ty = TableType::new(RefType::FUNCREF, 2, None);
77 /// let table = Table::new(&mut store, ty, Ref::Func(None))?;
78 ///
79 /// let module = Module::new(
80 /// &engine,
81 /// "(module
82 /// (table (import \"\" \"\") 2 funcref)
83 /// (func $f (result i32)
84 /// i32.const 10)
85 /// (elem (i32.const 0) $f)
86 /// )"
87 /// )?;
88 ///
89 /// let instance = Instance::new(&mut store, &module, &[table.into()])?;
90 /// // ...
91 /// # Ok(())
92 /// # }
93 /// ```
new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table>94 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 /// Async variant of [`Table::new`].
103 ///
104 /// You must use this variant with [`Store`](`crate::Store`)s which have a
105 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
106 #[cfg(feature = "async")]
new_async( mut store: impl AsContextMut, ty: TableType, init: Ref, ) -> Result<Table>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
_new( store: &mut StoreOpaque, limiter: Option<&mut StoreResourceLimiter<'_>>, ty: TableType, init: Ref, ) -> Result<Table>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 /// Returns the underlying type of this table, including its element type as
128 /// well as the maximum/minimum lower bounds.
129 ///
130 /// # Panics
131 ///
132 /// Panics if `store` does not own this table.
ty(&self, store: impl AsContext) -> TableType133 pub fn ty(&self, store: impl AsContext) -> TableType {
134 self._ty(store.as_context().0)
135 }
136
_ty(&self, store: &StoreOpaque) -> TableType137 fn _ty(&self, store: &StoreOpaque) -> TableType {
138 TableType::from_wasmtime_table(store.engine(), self.wasmtime_ty(store))
139 }
140
141 /// Returns the `vm::Table` within `store` as well as the optional
142 /// `GcStore` in use within `store`.
143 ///
144 /// # Panics
145 ///
146 /// Panics if this table does not belong to `store`.
wasmtime_table<'a>( &self, store: &'a mut StoreOpaque, lazy_init_range: impl IntoIterator<Item = u64>, ) -> (&'a mut vm::Table, Option<&'a mut GcStore>)147 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 /// Returns the table element value at `index`.
163 ///
164 /// Returns `None` if `index` is out of bounds.
165 ///
166 /// # Panics
167 ///
168 /// Panics if `store` does not own this table.
get(&self, mut store: impl AsContextMut, index: u64) -> Option<Ref>169 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 // SAFETY: `store` owns this table, so therefore it owns all
177 // functions within the table too.
178 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 // TODO(#10248) Required to support stack switching in the embedder
202 // API.
203 TableElementType::Cont => panic!("unimplemented table for cont"),
204 }
205 }
206
207 /// Writes the `val` provided into `index` within this table.
208 ///
209 /// # Errors
210 ///
211 /// Returns an error if `index` is out of bounds, if `val` does not have
212 /// the right type to be stored in this table, or if `val` belongs to a
213 /// different store.
214 ///
215 /// # Panics
216 ///
217 /// Panics if `store` does not own this table.
set(&self, mut store: impl AsContextMut, index: u64, val: Ref) -> Result<()>218 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
set_(&self, store: &mut StoreOpaque, index: u64, val: Ref) -> Result<()>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 // Note that `unchecked_copy` should be ok as we're under an
234 // `AutoAssertNoGc` which means that despite this not being
235 // rooted we don't have to worry about it going away.
236 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 // TODO(#10248) Required to support stack switching in the embedder
241 // API.
242 TableElementType::Cont => bail!("unimplemented table for cont"),
243 }
244 Ok(())
245 }
246
247 /// Returns the current size of this table.
248 ///
249 /// # Panics
250 ///
251 /// Panics if `store` does not own this table.
size(&self, store: impl AsContext) -> u64252 pub fn size(&self, store: impl AsContext) -> u64 {
253 self._size(store.as_context().0)
254 }
255
_size(&self, store: &StoreOpaque) -> u64256 pub(crate) fn _size(&self, store: &StoreOpaque) -> u64 {
257 // unwrap here should be ok because the runtime should always guarantee
258 // that we can fit the number of elements in a 64-bit integer.
259 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap()
260 }
261
262 /// Grows the size of this table by `delta` more elements, initialization
263 /// all new elements to `init`.
264 ///
265 /// Returns the previous size of this table if successful.
266 ///
267 /// # Errors
268 ///
269 /// Returns an error if the table cannot be grown by `delta`, for example
270 /// if it would cause the table to exceed its maximum size. Also returns an
271 /// error if `init` is not of the right type or if `init` does not belong to
272 /// `store`.
273 ///
274 /// This function also returns an error when used with a
275 /// [`Store`](`crate::Store`) which has a
276 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) (see also:
277 /// [`Store::limiter_async`](`crate::Store::limiter_async`)). When using an
278 /// async resource limiter, use [`Table::grow_async`] instead.
279 ///
280 /// # Panics
281 ///
282 /// Panics if `store` does not own this table.
grow(&self, mut store: impl AsContextMut, delta: u64, init: Ref) -> Result<u64>283 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
_grow<T>(&self, store: StoreContextMut<'_, T>, delta: u64, init: Ref) -> Result<u64>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 // SAFETY: in the context of `defined_table_grow` this
303 // is safe to call as it'll update the internal table
304 // pointer in the instance.
305 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 // SAFETY: in the context of `defined_table_grow` this
318 // is safe to call as it'll update the internal table
319 // pointer in the instance.
320 unsafe {
321 table
322 .grow_gc_ref(limiter, gc_store, delta, element.as_ref())
323 .await
324 }
325 })
326 .await?
327 }
328 // TODO(#10248) Required to support stack switching in the
329 // embedder API.
330 TableElementType::Cont => bail!("unimplemented table for cont"),
331 };
332 match result {
333 Some(size) => {
334 // unwrap here should be ok because the runtime should always
335 // guarantee that we can fit the table size in a 64-bit integer.
336 Ok(u64::try_from(size).unwrap())
337 }
338 None => bail!("failed to grow table by `{delta}`"),
339 }
340 }
341
342 /// Async variant of [`Table::grow`].
343 ///
344 /// Required when using a
345 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`).
346 ///
347 /// # Panics
348 ///
349 /// This function will panic when if the store doesn't own the table.
350 #[cfg(feature = "async")]
grow_async( &self, mut store: impl AsContextMut, delta: u64, init: Ref, ) -> Result<u64>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 /// Copy `len` elements from `src_table[src_index..]` into
361 /// `dst_table[dst_index..]`.
362 ///
363 /// # Errors
364 ///
365 /// Returns an error if the range is out of bounds of either the source or
366 /// destination tables, or if the source table's element type does not match
367 /// the destination table's element type.
368 ///
369 /// # Panics
370 ///
371 /// Panics if `store` does not own either `dst_table` or `src_table`.
copy( mut store: impl AsContextMut, dst_table: &Table, dst_index: u64, src_table: &Table, src_index: u64, len: u64, ) -> Result<()>372 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 // SAFETY: the the two tables have the same type, as type-checked above.
393 unsafe {
394 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?;
395 }
396 Ok(())
397 }
398
399 /// Copies the elements of `src_table` to `dst_table`.
400 ///
401 /// # Panics
402 ///
403 /// Panics if the either table doesn't belong to `store`.
404 ///
405 /// # Safety
406 ///
407 /// Requires that the two tables have previously been type-checked to have
408 /// the same type.
copy_raw( store: &mut StoreOpaque, dst_table: &Table, dst_index: u64, src_table: &Table, src_index: u64, len: u64, ) -> Result<(), Trap>409 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 // Handle lazy initialization of the source table first before doing
418 // anything else.
419 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX));
420 src_table.wasmtime_table(store, src_range);
421
422 // validate `dst_table` belongs to `store`.
423 dst_table.wasmtime_table(store, iter::empty());
424
425 // Figure out which of the three cases we're in:
426 //
427 // 1. Cross-instance table copy.
428 // 2. Intra-instance table copy.
429 // 3. Intra-table copy.
430 //
431 // We handle each of them slightly differently.
432 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 // 1. Cross-instance table copy: split the mutable store borrow into
439 // two mutable instance borrows, get each instance's defined table,
440 // and do the copy.
441 (false, _) => {
442 // SAFETY: accessing two instances mutably at the same time
443 // requires only accessing defined entities on each instance
444 // which is done below with `get_defined_*` methods.
445 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 // 2. Intra-instance, distinct-tables copy: split the mutable
458 // instance borrow into two distinct mutable table borrows and do
459 // the copy.
460 (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 // 3. Intra-table copy: get the table and copy within it!
470 (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 /// Fill `table[dst..(dst + len)]` with the given value.
480 ///
481 /// # Errors
482 ///
483 /// Returns an error if
484 ///
485 /// * `val` is not of the same type as this table's
486 /// element type,
487 ///
488 /// * the region to be filled is out of bounds, or
489 ///
490 /// * `val` comes from a different `Store` from this table.
491 ///
492 /// # Panics
493 ///
494 /// Panics if `store` does not own either `dst_table` or `src_table`.
fill(&self, mut store: impl AsContextMut, dst: u64, val: Ref, len: u64) -> Result<()>495 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
_fill( &self, store: &mut StoreOpaque, dst: u64, val: Ref, len: u64, ) -> Result<()>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 // Note that `val` is a `VMGcRef` temporarily read from the
515 // store here, and blocking GC with `AutoAssertNoGc` should
516 // ensure that it's not collected while being worked on here.
517 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 // TODO(#10248) Required to support stack switching in the embedder
524 // API.
525 TableElementType::Cont => bail!("unimplemented table for cont"),
526 }
527
528 Ok(())
529 }
530
531 #[cfg(feature = "gc")]
trace_roots(&self, store: &mut StoreOpaque, gc_roots_list: &mut vm::GcRootsList)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
from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table551 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table {
552 Table { instance, index }
553 }
554
wasmtime_ty<'a>(&self, store: &'a StoreOpaque) -> &'a wasmtime_environ::Table555 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
vmimport(&self, store: &StoreOpaque) -> vm::VMTableImport561 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
comes_from_same_store(&self, store: &StoreOpaque) -> bool570 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
571 store.id() == self.instance.store_id()
572 }
573
574 /// Returns a stable identifier for this table within its store.
575 ///
576 /// This allows distinguishing tables when introspecting them
577 /// e.g. via debug APIs.
578 #[cfg(feature = "debug")]
debug_index_in_store(&self) -> u64579 pub fn debug_index_in_store(&self) -> u64 {
580 u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
581 }
582
583 /// Get a stable hash key for this table.
584 ///
585 /// Even if the same underlying table definition is added to the
586 /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s,
587 /// this hash key will be consistent across all of these tables.
588 #[cfg_attr(
589 not(test),
590 expect(dead_code, reason = "Not used yet, but added for consistency")
591 )]
hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_>592 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> {
593 store[self.instance].table_ptr(self.index).as_ptr().addr()
594 }
595 }
596
element_type(ty: &TableType) -> TableElementType597 fn element_type(ty: &TableType) -> TableElementType {
598 match ty.element().heap_type().top() {
599 HeapType::Func => TableElementType::Func,
600 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef,
601 HeapType::Cont => TableElementType::Cont,
602 _ => unreachable!(),
603 }
604 }
605
606 impl Ref {
into_table_func( self, store: &mut StoreOpaque, ty: &RefType, ) -> Result<Option<NonNull<VMFuncRef>>>607 fn into_table_func(
608 self,
609 store: &mut StoreOpaque,
610 ty: &RefType,
611 ) -> Result<Option<NonNull<VMFuncRef>>> {
612 self.ensure_matches_ty(store, &ty)
613 .context("type mismatch: value does not match table element type")?;
614
615 match (self, ty.heap_type().top()) {
616 (Ref::Func(None), HeapType::Func) => {
617 assert!(ty.is_nullable());
618 Ok(None)
619 }
620 (Ref::Func(Some(f)), HeapType::Func) => {
621 debug_assert!(
622 f.comes_from_same_store(store),
623 "checked in `ensure_matches_ty`"
624 );
625 Ok(Some(f.vm_func_ref(store)))
626 }
627
628 _ => unreachable!("checked that the value matches the type above"),
629 }
630 }
631
into_table_gc_ref<'a>( self, store: &'a mut AutoAssertNoGc<'_>, ty: &RefType, ) -> Result<Option<&'a VMGcRef>>632 fn into_table_gc_ref<'a>(
633 self,
634 store: &'a mut AutoAssertNoGc<'_>,
635 ty: &RefType,
636 ) -> Result<Option<&'a VMGcRef>> {
637 self.ensure_matches_ty(store, &ty)
638 .context("type mismatch: value does not match table element type")?;
639
640 match (self, ty.heap_type().top()) {
641 (Ref::Extern(e), HeapType::Extern) => match e {
642 None => {
643 assert!(ty.is_nullable());
644 Ok(None)
645 }
646 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
647 },
648
649 (Ref::Any(a), HeapType::Any) => match a {
650 None => {
651 assert!(ty.is_nullable());
652 Ok(None)
653 }
654 Some(a) => Ok(Some(a.try_gc_ref(store)?)),
655 },
656
657 (Ref::Exn(e), HeapType::Exn) => match e {
658 None => {
659 assert!(ty.is_nullable());
660 Ok(None)
661 }
662 Some(e) => Ok(Some(e.try_gc_ref(store)?)),
663 },
664
665 _ => unreachable!("checked that the value matches the type above"),
666 }
667 }
668 }
669
670 #[cfg(test)]
671 mod tests {
672 use super::*;
673 use crate::{Instance, Module, Store};
674
675 #[test]
hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()>676 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> {
677 let mut store = Store::<()>::default();
678 let module = Module::new(
679 store.engine(),
680 r#"
681 (module
682 (table (export "t") 1 1 externref)
683 )
684 "#,
685 )?;
686 let instance = Instance::new(&mut store, &module, &[])?;
687
688 // Each time we `get_table`, we call `Table::from_wasmtime` which adds
689 // a new entry to `StoreData`, so `t1` and `t2` will have different
690 // indices into `StoreData`.
691 let t1 = instance.get_table(&mut store, "t").unwrap();
692 let t2 = instance.get_table(&mut store, "t").unwrap();
693
694 // That said, they really point to the same table.
695 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none());
696 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none());
697 let e = ExternRef::new(&mut store, 42)?;
698 t1.set(&mut store, 0, e.into())?;
699 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some());
700 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some());
701
702 // And therefore their hash keys are the same.
703 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0));
704
705 // But the hash keys are different from different tables.
706 let instance2 = Instance::new(&mut store, &module, &[])?;
707 let t3 = instance2.get_table(&mut store, "t").unwrap();
708 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0));
709
710 Ok(())
711 }
712
713 #[test]
grow_is_send()714 fn grow_is_send() {
715 fn _assert_send<T: Send>(_: T) {}
716 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) {
717 _assert_send(table.grow(store, 0, init))
718 }
719 }
720 }
721