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