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 /// ``` 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")] 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 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. 133 pub fn ty(&self, store: impl AsContext) -> TableType { 134 self._ty(store.as_context().0) 135 } 136 137 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`. 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. 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. 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 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. 252 pub fn size(&self, store: impl AsContext) -> u64 { 253 self._size(store.as_context().0) 254 } 255 256 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. 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 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 doens't own the table. 350 #[cfg(feature = "async")] 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`. 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. 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`. 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 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")] 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 551 pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedTableIndex) -> Table { 552 Table { instance, index } 553 } 554 555 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 561 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 570 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 571 store.id() == self.instance.store_id() 572 } 573 574 /// Get a stable hash key for this table. 575 /// 576 /// Even if the same underlying table definition is added to the 577 /// `StoreData` multiple times and becomes multiple `wasmtime::Table`s, 578 /// this hash key will be consistent across all of these tables. 579 #[cfg_attr( 580 not(test), 581 expect(dead_code, reason = "Not used yet, but added for consistency") 582 )] 583 pub(crate) fn hash_key(&self, store: &StoreOpaque) -> impl core::hash::Hash + Eq + use<'_> { 584 store[self.instance].table_ptr(self.index).as_ptr().addr() 585 } 586 } 587 588 fn element_type(ty: &TableType) -> TableElementType { 589 match ty.element().heap_type().top() { 590 HeapType::Func => TableElementType::Func, 591 HeapType::Exn | HeapType::Extern | HeapType::Any => TableElementType::GcRef, 592 HeapType::Cont => TableElementType::Cont, 593 _ => unreachable!(), 594 } 595 } 596 597 impl Ref { 598 fn into_table_func( 599 self, 600 store: &mut StoreOpaque, 601 ty: &RefType, 602 ) -> Result<Option<NonNull<VMFuncRef>>> { 603 self.ensure_matches_ty(store, &ty) 604 .context("type mismatch: value does not match table element type")?; 605 606 match (self, ty.heap_type().top()) { 607 (Ref::Func(None), HeapType::Func) => { 608 assert!(ty.is_nullable()); 609 Ok(None) 610 } 611 (Ref::Func(Some(f)), HeapType::Func) => { 612 debug_assert!( 613 f.comes_from_same_store(store), 614 "checked in `ensure_matches_ty`" 615 ); 616 Ok(Some(f.vm_func_ref(store))) 617 } 618 619 _ => unreachable!("checked that the value matches the type above"), 620 } 621 } 622 623 fn into_table_gc_ref<'a>( 624 self, 625 store: &'a mut AutoAssertNoGc<'_>, 626 ty: &RefType, 627 ) -> Result<Option<&'a VMGcRef>> { 628 self.ensure_matches_ty(store, &ty) 629 .context("type mismatch: value does not match table element type")?; 630 631 match (self, ty.heap_type().top()) { 632 (Ref::Extern(e), HeapType::Extern) => match e { 633 None => { 634 assert!(ty.is_nullable()); 635 Ok(None) 636 } 637 Some(e) => Ok(Some(e.try_gc_ref(store)?)), 638 }, 639 640 (Ref::Any(a), HeapType::Any) => match a { 641 None => { 642 assert!(ty.is_nullable()); 643 Ok(None) 644 } 645 Some(a) => Ok(Some(a.try_gc_ref(store)?)), 646 }, 647 648 (Ref::Exn(e), HeapType::Exn) => match e { 649 None => { 650 assert!(ty.is_nullable()); 651 Ok(None) 652 } 653 Some(e) => Ok(Some(e.try_gc_ref(store)?)), 654 }, 655 656 _ => unreachable!("checked that the value matches the type above"), 657 } 658 } 659 } 660 661 #[cfg(test)] 662 mod tests { 663 use super::*; 664 use crate::{Instance, Module, Store}; 665 666 #[test] 667 fn hash_key_is_stable_across_duplicate_store_data_entries() -> Result<()> { 668 let mut store = Store::<()>::default(); 669 let module = Module::new( 670 store.engine(), 671 r#" 672 (module 673 (table (export "t") 1 1 externref) 674 ) 675 "#, 676 )?; 677 let instance = Instance::new(&mut store, &module, &[])?; 678 679 // Each time we `get_table`, we call `Table::from_wasmtime` which adds 680 // a new entry to `StoreData`, so `t1` and `t2` will have different 681 // indices into `StoreData`. 682 let t1 = instance.get_table(&mut store, "t").unwrap(); 683 let t2 = instance.get_table(&mut store, "t").unwrap(); 684 685 // That said, they really point to the same table. 686 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_none()); 687 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_none()); 688 let e = ExternRef::new(&mut store, 42)?; 689 t1.set(&mut store, 0, e.into())?; 690 assert!(t1.get(&mut store, 0).unwrap().unwrap_extern().is_some()); 691 assert!(t2.get(&mut store, 0).unwrap().unwrap_extern().is_some()); 692 693 // And therefore their hash keys are the same. 694 assert!(t1.hash_key(&store.as_context().0) == t2.hash_key(&store.as_context().0)); 695 696 // But the hash keys are different from different tables. 697 let instance2 = Instance::new(&mut store, &module, &[])?; 698 let t3 = instance2.get_table(&mut store, "t").unwrap(); 699 assert!(t1.hash_key(&store.as_context().0) != t3.hash_key(&store.as_context().0)); 700 701 Ok(()) 702 } 703 704 #[test] 705 fn grow_is_send() { 706 fn _assert_send<T: Send>(_: T) {} 707 fn _grow(table: &Table, store: &mut Store<()>, init: Ref) { 708 _assert_send(table.grow(store, 0, init)) 709 } 710 } 711 } 712