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 /// # Panics 63 /// 64 /// This function will panic when used with a [`Store`](`crate::Store`) 65 /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) 66 /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`). 67 /// When using an async resource limiter, use [`Table::new_async`] 68 /// instead. 69 /// 70 /// # Examples 71 /// 72 /// ``` 73 /// # use wasmtime::*; 74 /// # fn main() -> anyhow::Result<()> { 75 /// let engine = Engine::default(); 76 /// let mut store = Store::new(&engine, ()); 77 /// 78 /// let ty = TableType::new(RefType::FUNCREF, 2, None); 79 /// let table = Table::new(&mut store, ty, Ref::Func(None))?; 80 /// 81 /// let module = Module::new( 82 /// &engine, 83 /// "(module 84 /// (table (import \"\" \"\") 2 funcref) 85 /// (func $f (result i32) 86 /// i32.const 10) 87 /// (elem (i32.const 0) $f) 88 /// )" 89 /// )?; 90 /// 91 /// let instance = Instance::new(&mut store, &module, &[table.into()])?; 92 /// // ... 93 /// # Ok(()) 94 /// # } 95 /// ``` 96 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 /// Async variant of [`Table::new`]. You must use this variant with 103 /// [`Store`](`crate::Store`)s which have a 104 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). 105 /// 106 /// # Panics 107 /// 108 /// This function will panic when used with a non-async 109 /// [`Store`](`crate::Store`) 110 #[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 /// Returns the underlying type of this table, including its element type as 132 /// well as the maximum/minimum lower bounds. 133 /// 134 /// # Panics 135 /// 136 /// Panics if `store` does not own this table. 137 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 /// Returns the `vm::Table` within `store` as well as the optional 146 /// `GcStore` in use within `store`. 147 /// 148 /// # Panics 149 /// 150 /// Panics if this table does not belong to `store`. 151 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 /// Returns the table element value at `index`. 166 /// 167 /// Returns `None` if `index` is out of bounds. 168 /// 169 /// # Panics 170 /// 171 /// Panics if `store` does not own this table. 172 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 // SAFETY: `store` owns this table, so therefore it owns all 180 // functions within the table too. 181 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 // TODO(#10248) Required to support stack switching in the embedder 205 // API. 206 TableElementType::Cont => panic!("unimplemented table for cont"), 207 } 208 } 209 210 /// Writes the `val` provided into `index` within this table. 211 /// 212 /// # Errors 213 /// 214 /// Returns an error if `index` is out of bounds, if `val` does not have 215 /// the right type to be stored in this table, or if `val` belongs to a 216 /// different store. 217 /// 218 /// # Panics 219 /// 220 /// Panics if `store` does not own this table. 221 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 // Note that `unchecked_copy` should be ok as we're under an 237 // `AutoAssertNoGc` which means that despite this not being 238 // rooted we don't have to worry about it going away. 239 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 // TODO(#10248) Required to support stack switching in the embedder 244 // API. 245 TableElementType::Cont => bail!("unimplemented table for cont"), 246 } 247 Ok(()) 248 } 249 250 /// Returns the current size of this table. 251 /// 252 /// # Panics 253 /// 254 /// Panics if `store` does not own this table. 255 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 // unwrap here should be ok because the runtime should always guarantee 261 // that we can fit the number of elements in a 64-bit integer. 262 u64::try_from(store[self.instance].table(self.index).current_elements).unwrap() 263 } 264 265 /// Grows the size of this table by `delta` more elements, initialization 266 /// all new elements to `init`. 267 /// 268 /// Returns the previous size of this table if successful. 269 /// 270 /// # Errors 271 /// 272 /// Returns an error if the table cannot be grown by `delta`, for example 273 /// if it would cause the table to exceed its maximum size. Also returns an 274 /// error if `init` is not of the right type or if `init` does not belong to 275 /// `store`. 276 /// 277 /// # Panics 278 /// 279 /// Panics if `store` does not own this table. 280 /// 281 /// This function will panic when used with a [`Store`](`crate::Store`) 282 /// which has a [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`) 283 /// (see also: [`Store::limiter_async`](`crate::Store::limiter_async`)). 284 /// When using an async resource limiter, use [`Table::grow_async`] 285 /// instead. 286 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 // SAFETY: in the context of `defined_table_grow` this 305 // is safe to call as it'll update the internal table 306 // pointer in the instance. 307 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 // SAFETY: in the context of `defined_table_grow` this 320 // is safe to call as it'll update the internal table 321 // pointer in the instance. 322 unsafe { 323 table 324 .grow_gc_ref(limiter, gc_store, delta, element.as_ref()) 325 .await 326 } 327 }) 328 .await? 329 } 330 // TODO(#10248) Required to support stack switching in the 331 // embedder API. 332 TableElementType::Cont => bail!("unimplemented table for cont"), 333 }; 334 match result { 335 Some(size) => { 336 // unwrap here should be ok because the runtime should always 337 // guarantee that we can fit the table size in a 64-bit integer. 338 Ok(u64::try_from(size).unwrap()) 339 } 340 None => bail!("failed to grow table by `{}`", delta), 341 } 342 } 343 344 /// Async variant of [`Table::grow`]. Required when using a 345 /// [`ResourceLimiterAsync`](`crate::ResourceLimiterAsync`). 346 /// 347 /// # Panics 348 /// 349 /// This function will panic when used with a non-async 350 /// [`Store`](`crate::Store`). 351 #[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 /// Copy `len` elements from `src_table[src_index..]` into 362 /// `dst_table[dst_index..]`. 363 /// 364 /// # Errors 365 /// 366 /// Returns an error if the range is out of bounds of either the source or 367 /// destination tables, or if the source table's element type does not match 368 /// the destination table's element type. 369 /// 370 /// # Panics 371 /// 372 /// Panics if `store` does not own either `dst_table` or `src_table`. 373 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 // SAFETY: the the two tables have the same type, as type-checked above. 394 unsafe { 395 Self::copy_raw(store, dst_table, dst_index, src_table, src_index, len)?; 396 } 397 Ok(()) 398 } 399 400 /// Copies the elements of `src_table` to `dst_table`. 401 /// 402 /// # Panics 403 /// 404 /// Panics if the either table doesn't belong to `store`. 405 /// 406 /// # Safety 407 /// 408 /// Requires that the two tables have previously been type-checked to have 409 /// the same type. 410 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 // Handle lazy initialization of the source table first before doing 419 // anything else. 420 let src_range = src_index..(src_index.checked_add(len).unwrap_or(u64::MAX)); 421 src_table.wasmtime_table(store, src_range); 422 423 // validate `dst_table` belongs to `store`. 424 dst_table.wasmtime_table(store, iter::empty()); 425 426 // Figure out which of the three cases we're in: 427 // 428 // 1. Cross-instance table copy. 429 // 2. Intra-instance table copy. 430 // 3. Intra-table copy. 431 // 432 // We handle each of them slightly differently. 433 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 // 1. Cross-instance table copy: split the mutable store borrow into 440 // two mutable instance borrows, get each instance's defined table, 441 // and do the copy. 442 (false, _) => { 443 // SAFETY: accessing two instances mutably at the same time 444 // requires only accessing defined entities on each instance 445 // which is done below with `get_defined_*` methods. 446 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 // 2. Intra-instance, distinct-tables copy: split the mutable 459 // instance borrow into two distinct mutable table borrows and do 460 // the copy. 461 (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 // 3. Intra-table copy: get the table and copy within it! 471 (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 /// Fill `table[dst..(dst + len)]` with the given value. 481 /// 482 /// # Errors 483 /// 484 /// Returns an error if 485 /// 486 /// * `val` is not of the same type as this table's 487 /// element type, 488 /// 489 /// * the region to be filled is out of bounds, or 490 /// 491 /// * `val` comes from a different `Store` from this table. 492 /// 493 /// # Panics 494 /// 495 /// Panics if `store` does not own either `dst_table` or `src_table`. 496 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 // Note that `val` is a `VMGcRef` temporarily read from the 516 // store here, and blocking GC with `AutoAssertNoGc` should 517 // ensure that it's not collected while being worked on here. 518 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 // TODO(#10248) Required to support stack switching in the embedder 525 // API. 526 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 /// Get a stable hash key for this table. 576 /// 577 /// Even if the same underlying table definition is added to the 578 /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s, 579 /// this hash key will be consistent across all of these tables. 580 #[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 589 fn 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 598 impl 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)] 663 mod 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 // Each time we `get_table`, we call `Table::from_wasmtime` which adds 681 // a new entry to `StoreData`, so `t1` and `t2` will have different 682 // indices into `StoreData`. 683 let t1 = instance.get_table(&mut store, "t").unwrap(); 684 let t2 = instance.get_table(&mut store, "t").unwrap(); 685 686 // That said, they really point to the same table. 687 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 // And therefore their hash keys are the same. 695 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0)); 696 697 // But the hash keys are different from different tables. 698 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 } 713