1 //! Memory management for tables. 2 //! 3 //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. 4 5 #![cfg_attr(feature = "gc", allow(irrefutable_let_patterns))] 6 7 use crate::prelude::*; 8 use crate::runtime::vm::vmcontext::{VMFuncRef, VMTableDefinition}; 9 use crate::runtime::vm::{GcStore, SendSyncPtr, Store, VMGcRef}; 10 use core::cmp; 11 use core::ops::Range; 12 use core::ptr::{self, NonNull}; 13 use core::slice; 14 use sptr::Strict; 15 use wasmtime_environ::{ 16 TablePlan, TableStyle, Trap, WasmHeapTopType, WasmRefType, FUNCREF_INIT_BIT, FUNCREF_MASK, 17 }; 18 19 /// An element going into or coming out of a table. 20 /// 21 /// Table elements are stored as pointers and are default-initialized with 22 /// `ptr::null_mut`. 23 pub enum TableElement { 24 /// A `funcref`. 25 FuncRef(*mut VMFuncRef), 26 27 /// A GC reference. 28 GcRef(Option<VMGcRef>), 29 30 /// An uninitialized funcref value. This should never be exposed 31 /// beyond the `wasmtime` crate boundary; the upper-level code 32 /// (which has access to the info needed for lazy initialization) 33 /// will replace it when fetched. 34 UninitFunc, 35 } 36 37 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 38 pub enum TableElementType { 39 Func, 40 GcRef, 41 } 42 43 impl TableElementType { 44 fn matches(&self, val: &TableElement) -> bool { 45 match (val, self) { 46 (TableElement::FuncRef(_), TableElementType::Func) => true, 47 (TableElement::GcRef(_), TableElementType::GcRef) => true, 48 _ => false, 49 } 50 } 51 } 52 53 // The usage of `*mut VMFuncRef` is safe w.r.t. thread safety, this just relies 54 // on thread-safety of `VMGcRef` itself. 55 unsafe impl Send for TableElement where VMGcRef: Send {} 56 unsafe impl Sync for TableElement where VMGcRef: Sync {} 57 58 impl TableElement { 59 /// Consumes a table element into a pointer/reference, as it 60 /// exists outside the table itself. This strips off any tag bits 61 /// or other information that only lives inside the table. 62 /// 63 /// Can only be done to an initialized table element; lazy init 64 /// must occur first. (In other words, lazy values do not survive 65 /// beyond the table, as every table read path initializes them.) 66 /// 67 /// # Safety 68 /// 69 /// The same warnings as for `into_table_values()` apply. 70 pub(crate) unsafe fn into_func_ref_asserting_initialized(self) -> *mut VMFuncRef { 71 match self { 72 Self::FuncRef(e) => e, 73 Self::UninitFunc => panic!("Uninitialized table element value outside of table slot"), 74 Self::GcRef(_) => panic!("GC reference is not a function reference"), 75 } 76 } 77 78 /// Indicates whether this value is the "uninitialized element" 79 /// value. 80 pub(crate) fn is_uninit(&self) -> bool { 81 match self { 82 Self::UninitFunc => true, 83 _ => false, 84 } 85 } 86 } 87 88 impl From<*mut VMFuncRef> for TableElement { 89 fn from(f: *mut VMFuncRef) -> TableElement { 90 TableElement::FuncRef(f) 91 } 92 } 93 94 impl From<Option<VMGcRef>> for TableElement { 95 fn from(x: Option<VMGcRef>) -> TableElement { 96 TableElement::GcRef(x) 97 } 98 } 99 100 impl From<VMGcRef> for TableElement { 101 fn from(x: VMGcRef) -> TableElement { 102 TableElement::GcRef(Some(x)) 103 } 104 } 105 106 #[derive(Copy, Clone)] 107 #[repr(transparent)] 108 struct TaggedFuncRef(*mut VMFuncRef); 109 110 impl TaggedFuncRef { 111 const UNINIT: TaggedFuncRef = TaggedFuncRef(ptr::null_mut()); 112 113 /// Converts the given `ptr`, a valid funcref pointer, into a tagged pointer 114 /// by adding in the `FUNCREF_INIT_BIT`. 115 fn from(ptr: *mut VMFuncRef, lazy_init: bool) -> Self { 116 if lazy_init { 117 let masked = Strict::map_addr(ptr, |a| a | FUNCREF_INIT_BIT); 118 TaggedFuncRef(masked) 119 } else { 120 TaggedFuncRef(ptr) 121 } 122 } 123 124 /// Converts a tagged pointer into a `TableElement`, returning `UninitFunc` 125 /// for null (not a tagged value) or `FuncRef` for otherwise tagged values. 126 fn into_table_element(self, lazy_init: bool) -> TableElement { 127 let ptr = self.0; 128 if lazy_init && ptr.is_null() { 129 TableElement::UninitFunc 130 } else { 131 // Masking off the tag bit is harmless whether the table uses lazy 132 // init or not. 133 let unmasked = Strict::map_addr(ptr, |a| a & FUNCREF_MASK); 134 TableElement::FuncRef(unmasked) 135 } 136 } 137 } 138 139 pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>; 140 141 pub enum StaticTable { 142 Func(StaticFuncTable), 143 GcRef(StaticGcRefTable), 144 } 145 146 impl From<StaticFuncTable> for StaticTable { 147 fn from(value: StaticFuncTable) -> Self { 148 Self::Func(value) 149 } 150 } 151 152 impl From<StaticGcRefTable> for StaticTable { 153 fn from(value: StaticGcRefTable) -> Self { 154 Self::GcRef(value) 155 } 156 } 157 158 pub struct StaticFuncTable { 159 /// Where data for this table is stored. The length of this list is the 160 /// maximum size of the table. 161 data: SendSyncPtr<[FuncTableElem]>, 162 /// The current size of the table. 163 size: u32, 164 /// Whether elements of this table are initialized lazily. 165 lazy_init: bool, 166 } 167 168 pub struct StaticGcRefTable { 169 /// Where data for this table is stored. The length of this list is the 170 /// maximum size of the table. 171 data: SendSyncPtr<[Option<VMGcRef>]>, 172 /// The current size of the table. 173 size: u32, 174 } 175 176 pub enum DynamicTable { 177 Func(DynamicFuncTable), 178 GcRef(DynamicGcRefTable), 179 } 180 181 impl From<DynamicFuncTable> for DynamicTable { 182 fn from(value: DynamicFuncTable) -> Self { 183 Self::Func(value) 184 } 185 } 186 187 impl From<DynamicGcRefTable> for DynamicTable { 188 fn from(value: DynamicGcRefTable) -> Self { 189 Self::GcRef(value) 190 } 191 } 192 193 pub struct DynamicFuncTable { 194 /// Dynamically managed storage space for this table. The length of this 195 /// vector is the current size of the table. 196 elements: Vec<FuncTableElem>, 197 /// Maximum size that `elements` can grow to. 198 maximum: Option<u32>, 199 /// Whether elements of this table are initialized lazily. 200 lazy_init: bool, 201 } 202 203 pub struct DynamicGcRefTable { 204 /// Dynamically managed storage space for this table. The length of this 205 /// vector is the current size of the table. 206 elements: Vec<Option<VMGcRef>>, 207 /// Maximum size that `elements` can grow to. 208 maximum: Option<u32>, 209 } 210 211 /// Represents an instance's table. 212 pub enum Table { 213 /// A "static" table where storage space is managed externally, currently 214 /// used with the pooling allocator. 215 Static(StaticTable), 216 /// A "dynamic" table where table storage space is dynamically allocated via 217 /// `malloc` (aka Rust's `Vec`). 218 Dynamic(DynamicTable), 219 } 220 221 impl From<StaticTable> for Table { 222 fn from(value: StaticTable) -> Self { 223 Self::Static(value) 224 } 225 } 226 227 impl From<StaticFuncTable> for Table { 228 fn from(value: StaticFuncTable) -> Self { 229 let t: StaticTable = value.into(); 230 t.into() 231 } 232 } 233 234 impl From<StaticGcRefTable> for Table { 235 fn from(value: StaticGcRefTable) -> Self { 236 let t: StaticTable = value.into(); 237 t.into() 238 } 239 } 240 241 impl From<DynamicTable> for Table { 242 fn from(value: DynamicTable) -> Self { 243 Self::Dynamic(value) 244 } 245 } 246 247 impl From<DynamicFuncTable> for Table { 248 fn from(value: DynamicFuncTable) -> Self { 249 let t: DynamicTable = value.into(); 250 t.into() 251 } 252 } 253 254 impl From<DynamicGcRefTable> for Table { 255 fn from(value: DynamicGcRefTable) -> Self { 256 let t: DynamicTable = value.into(); 257 t.into() 258 } 259 } 260 261 fn wasm_to_table_type(ty: WasmRefType) -> TableElementType { 262 match ty.heap_type.top() { 263 WasmHeapTopType::Func => TableElementType::Func, 264 WasmHeapTopType::Any | WasmHeapTopType::Extern => TableElementType::GcRef, 265 } 266 } 267 268 impl Table { 269 /// Create a new dynamic (movable) table instance for the specified table plan. 270 pub fn new_dynamic(plan: &TablePlan, store: &mut dyn Store) -> Result<Self> { 271 Self::limit_new(plan, store)?; 272 let TableStyle::CallerChecksSignature { lazy_init } = plan.style; 273 match wasm_to_table_type(plan.table.wasm_ty) { 274 TableElementType::Func => Ok(Self::from(DynamicFuncTable { 275 elements: vec![None; usize::try_from(plan.table.minimum).unwrap()], 276 maximum: plan.table.maximum, 277 lazy_init, 278 })), 279 TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable { 280 elements: (0..usize::try_from(plan.table.minimum).unwrap()) 281 .map(|_| None) 282 .collect(), 283 maximum: plan.table.maximum, 284 })), 285 } 286 } 287 288 /// Create a new static (immovable) table instance for the specified table plan. 289 pub unsafe fn new_static( 290 plan: &TablePlan, 291 data: SendSyncPtr<[u8]>, 292 store: &mut dyn Store, 293 ) -> Result<Self> { 294 Self::limit_new(plan, store)?; 295 296 let size = plan.table.minimum; 297 let max = plan 298 .table 299 .maximum 300 .map_or(usize::MAX, |x| usize::try_from(x).unwrap()); 301 302 match wasm_to_table_type(plan.table.wasm_ty) { 303 TableElementType::Func => { 304 let len = { 305 let data = data.as_non_null().as_ref(); 306 let (before, data, after) = data.align_to::<FuncTableElem>(); 307 assert!(before.is_empty()); 308 assert!(after.is_empty()); 309 data.len() 310 }; 311 ensure!( 312 usize::try_from(plan.table.minimum).unwrap() <= len, 313 "initial table size of {} exceeds the pooling allocator's \ 314 configured maximum table size of {len} elements", 315 plan.table.minimum, 316 ); 317 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts( 318 data.as_non_null().cast::<FuncTableElem>(), 319 cmp::min(len, max), 320 )); 321 let TableStyle::CallerChecksSignature { lazy_init } = plan.style; 322 Ok(Self::from(StaticFuncTable { 323 data, 324 size, 325 lazy_init, 326 })) 327 } 328 TableElementType::GcRef => { 329 let len = { 330 let data = data.as_non_null().as_ref(); 331 let (before, data, after) = data.align_to::<Option<VMGcRef>>(); 332 assert!(before.is_empty()); 333 assert!(after.is_empty()); 334 data.len() 335 }; 336 ensure!( 337 usize::try_from(plan.table.minimum).unwrap() <= len, 338 "initial table size of {} exceeds the pooling allocator's \ 339 configured maximum table size of {len} elements", 340 plan.table.minimum, 341 ); 342 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts( 343 data.as_non_null().cast::<Option<VMGcRef>>(), 344 cmp::min(len, max), 345 )); 346 Ok(Self::from(StaticGcRefTable { data, size })) 347 } 348 } 349 } 350 351 fn limit_new(plan: &TablePlan, store: &mut dyn Store) -> Result<()> { 352 if !store.table_growing(0, plan.table.minimum, plan.table.maximum)? { 353 bail!( 354 "table minimum size of {} elements exceeds table limits", 355 plan.table.minimum 356 ); 357 } 358 Ok(()) 359 } 360 361 /// Returns the type of the elements in this table. 362 pub fn element_type(&self) -> TableElementType { 363 match self { 364 Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => { 365 TableElementType::Func 366 } 367 Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => { 368 TableElementType::GcRef 369 } 370 } 371 } 372 373 /// Returns whether or not the underlying storage of the table is "static". 374 #[cfg(feature = "pooling-allocator")] 375 pub(crate) fn is_static(&self) -> bool { 376 matches!(self, Table::Static(_)) 377 } 378 379 /// Returns the number of allocated elements. 380 pub fn size(&self) -> u32 { 381 match self { 382 Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size, 383 Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size, 384 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => { 385 elements.len().try_into().unwrap() 386 } 387 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => { 388 elements.len().try_into().unwrap() 389 } 390 } 391 } 392 393 /// Returns the maximum number of elements at runtime. 394 /// 395 /// Returns `None` if the table is unbounded. 396 /// 397 /// The runtime maximum may not be equal to the maximum from the table's Wasm type 398 /// when it is being constrained by an instance allocator. 399 pub fn maximum(&self) -> Option<u32> { 400 match self { 401 Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => { 402 Some(u32::try_from(data.len()).unwrap()) 403 } 404 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => { 405 Some(u32::try_from(data.len()).unwrap()) 406 } 407 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum, 408 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum, 409 } 410 } 411 412 /// Initializes the contents of this table to the specified function 413 /// 414 /// # Panics 415 /// 416 /// Panics if the table is not a function table. 417 pub fn init_func( 418 &mut self, 419 dst: u32, 420 items: impl ExactSizeIterator<Item = *mut VMFuncRef>, 421 ) -> Result<(), Trap> { 422 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?; 423 424 let (funcrefs, lazy_init) = self.funcrefs_mut(); 425 let elements = funcrefs 426 .get_mut(dst..) 427 .and_then(|s| s.get_mut(..items.len())) 428 .ok_or(Trap::TableOutOfBounds)?; 429 430 for (item, slot) in items.zip(elements) { 431 *slot = TaggedFuncRef::from(item, lazy_init); 432 } 433 Ok(()) 434 } 435 436 /// Fill `table[dst..]` with values from `items` 437 /// 438 /// Returns a trap error on out-of-bounds accesses. 439 pub fn init_gc_refs( 440 &mut self, 441 dst: u32, 442 items: impl ExactSizeIterator<Item = Option<VMGcRef>>, 443 ) -> Result<(), Trap> { 444 let dst = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?; 445 446 let elements = self 447 .gc_refs_mut() 448 .get_mut(dst..) 449 .and_then(|s| s.get_mut(..items.len())) 450 .ok_or(Trap::TableOutOfBounds)?; 451 452 for (item, slot) in items.zip(elements) { 453 *slot = item; 454 } 455 Ok(()) 456 } 457 458 /// Fill `table[dst..dst + len]` with `val`. 459 /// 460 /// Returns a trap error on out-of-bounds accesses. 461 /// 462 /// # Panics 463 /// 464 /// Panics if `val` does not have a type that matches this table. 465 pub fn fill( 466 &mut self, 467 gc_store: &mut GcStore, 468 dst: u32, 469 val: TableElement, 470 len: u32, 471 ) -> Result<(), Trap> { 472 let start = dst as usize; 473 let end = start 474 .checked_add(len as usize) 475 .ok_or_else(|| Trap::TableOutOfBounds)?; 476 477 if end > self.size() as usize { 478 return Err(Trap::TableOutOfBounds); 479 } 480 481 match val { 482 TableElement::FuncRef(f) => { 483 let (funcrefs, lazy_init) = self.funcrefs_mut(); 484 funcrefs[start..end].fill(TaggedFuncRef::from(f, lazy_init)); 485 } 486 TableElement::GcRef(r) => { 487 // Clone the init GC reference into each table slot. 488 for slot in &mut self.gc_refs_mut()[start..end] { 489 gc_store.write_gc_ref(slot, r.as_ref()); 490 } 491 492 // Drop the init GC reference, since we aren't holding onto this 493 // reference anymore, only the clones in the table. 494 if let Some(r) = r { 495 gc_store.drop_gc_ref(r); 496 } 497 } 498 TableElement::UninitFunc => { 499 let (funcrefs, _lazy_init) = self.funcrefs_mut(); 500 funcrefs[start..end].fill(TaggedFuncRef::UNINIT); 501 } 502 } 503 504 Ok(()) 505 } 506 507 /// Grow table by the specified amount of elements. 508 /// 509 /// Returns the previous size of the table if growth is successful. 510 /// 511 /// Returns `None` if table can't be grown by the specified amount of 512 /// elements, or if the `init_value` is the wrong kind of table element. 513 /// 514 /// # Panics 515 /// 516 /// Panics if `init_value` does not have a type that matches this table. 517 /// 518 /// # Unsafety 519 /// 520 /// Resizing the table can reallocate its internal elements buffer. This 521 /// table's instance's `VMContext` has raw pointers to the elements buffer 522 /// that are used by Wasm, and they need to be fixed up before we call into 523 /// Wasm again. Failure to do so will result in use-after-free inside Wasm. 524 /// 525 /// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates 526 /// this unsafety. 527 pub unsafe fn grow( 528 &mut self, 529 delta: u32, 530 init_value: TableElement, 531 store: &mut dyn Store, 532 ) -> Result<Option<u32>, Error> { 533 let old_size = self.size(); 534 535 // Don't try to resize the table if its size isn't changing, just return 536 // success. 537 if delta == 0 { 538 return Ok(Some(old_size)); 539 } 540 541 let new_size = match old_size.checked_add(delta) { 542 Some(s) => s, 543 None => { 544 store.table_grow_failed(format_err!("overflow calculating new table size"))?; 545 return Ok(None); 546 } 547 }; 548 549 if !store.table_growing(old_size, new_size, self.maximum())? { 550 return Ok(None); 551 } 552 553 // The WebAssembly spec requires failing a `table.grow` request if 554 // it exceeds the declared limits of the table. We may have set lower 555 // limits in the instance allocator as well. 556 if let Some(max) = self.maximum() { 557 if new_size > max { 558 store.table_grow_failed(format_err!("Table maximum size exceeded"))?; 559 return Ok(None); 560 } 561 } 562 563 debug_assert!(self.type_matches(&init_value)); 564 565 // First resize the storage and then fill with the init value 566 match self { 567 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => { 568 unsafe { 569 debug_assert!(data.as_ref()[*size as usize..new_size as usize] 570 .iter() 571 .all(|x| x.is_none())); 572 } 573 *size = new_size; 574 } 575 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => { 576 unsafe { 577 debug_assert!(data.as_ref()[*size as usize..new_size as usize] 578 .iter() 579 .all(|x| x.is_none())); 580 } 581 *size = new_size; 582 } 583 584 // These calls to `resize` could move the base address of 585 // `elements`. If this table's limits declare it to be fixed-size, 586 // then during AOT compilation we may have promised Cranelift that 587 // the table base address won't change, so it is allowed to optimize 588 // loading the base address. However, in that case the above checks 589 // that delta is non-zero and the new size doesn't exceed the 590 // maximum mean we can't get here. 591 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => { 592 elements.resize(usize::try_from(new_size).unwrap(), None); 593 } 594 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => { 595 elements.resize_with(usize::try_from(new_size).unwrap(), || None); 596 } 597 } 598 599 self.fill(store.gc_store(), old_size, init_value, delta) 600 .expect("table should not be out of bounds"); 601 602 Ok(Some(old_size)) 603 } 604 605 /// Get reference to the specified element. 606 /// 607 /// Returns `None` if the index is out of bounds. 608 pub fn get(&self, gc_store: &mut GcStore, index: u32) -> Option<TableElement> { 609 let index = usize::try_from(index).ok()?; 610 match self.element_type() { 611 TableElementType::Func => { 612 let (funcrefs, lazy_init) = self.funcrefs(); 613 funcrefs 614 .get(index) 615 .copied() 616 .map(|e| e.into_table_element(lazy_init)) 617 } 618 TableElementType::GcRef => self.gc_refs().get(index).map(|r| { 619 let r = r.as_ref().map(|r| gc_store.clone_gc_ref(r)); 620 TableElement::GcRef(r) 621 }), 622 } 623 } 624 625 /// Set reference to the specified element. 626 /// 627 /// # Errors 628 /// 629 /// Returns an error if `index` is out of bounds or if this table type does 630 /// not match the element type. 631 /// 632 /// # Panics 633 /// 634 /// Panics if `elem` is not of the right type for this table. 635 pub fn set(&mut self, index: u32, elem: TableElement) -> Result<(), ()> { 636 let index = usize::try_from(index).map_err(|_| ())?; 637 match elem { 638 TableElement::FuncRef(f) => { 639 let (funcrefs, lazy_init) = self.funcrefs_mut(); 640 *funcrefs.get_mut(index).ok_or(())? = TaggedFuncRef::from(f, lazy_init); 641 } 642 TableElement::UninitFunc => { 643 let (funcrefs, _lazy_init) = self.funcrefs_mut(); 644 *funcrefs.get_mut(index).ok_or(())? = TaggedFuncRef::UNINIT; 645 } 646 TableElement::GcRef(e) => { 647 *self.gc_refs_mut().get_mut(index).ok_or(())? = e; 648 } 649 } 650 Ok(()) 651 } 652 653 /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. 654 /// 655 /// # Errors 656 /// 657 /// Returns an error if the range is out of bounds of either the source or 658 /// destination tables. 659 pub unsafe fn copy( 660 gc_store: &mut GcStore, 661 dst_table: *mut Self, 662 src_table: *mut Self, 663 dst_index: u32, 664 src_index: u32, 665 len: u32, 666 ) -> Result<(), Trap> { 667 // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy 668 669 if src_index 670 .checked_add(len) 671 .map_or(true, |n| n > (*src_table).size()) 672 || dst_index 673 .checked_add(len) 674 .map_or(true, |m| m > (*dst_table).size()) 675 { 676 return Err(Trap::TableOutOfBounds); 677 } 678 679 debug_assert!( 680 (*dst_table).element_type() == (*src_table).element_type(), 681 "table element type mismatch" 682 ); 683 684 let src_range = src_index as usize..src_index as usize + len as usize; 685 let dst_range = dst_index as usize..dst_index as usize + len as usize; 686 687 // Check if the tables are the same as we cannot mutably borrow and also borrow the same `RefCell` 688 if ptr::eq(dst_table, src_table) { 689 (*dst_table).copy_elements_within(gc_store, dst_range, src_range); 690 } else { 691 Self::copy_elements(gc_store, &mut *dst_table, &*src_table, dst_range, src_range); 692 } 693 694 Ok(()) 695 } 696 697 /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. 698 pub fn vmtable(&mut self) -> VMTableDefinition { 699 match self { 700 Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => { 701 VMTableDefinition { 702 base: data.as_ptr().cast(), 703 current_elements: *size, 704 } 705 } 706 Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => { 707 VMTableDefinition { 708 base: data.as_ptr().cast(), 709 current_elements: *size, 710 } 711 } 712 Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => { 713 VMTableDefinition { 714 base: elements.as_mut_ptr().cast(), 715 current_elements: elements.len().try_into().unwrap(), 716 } 717 } 718 Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => { 719 VMTableDefinition { 720 base: elements.as_mut_ptr().cast(), 721 current_elements: elements.len().try_into().unwrap(), 722 } 723 } 724 } 725 } 726 727 fn type_matches(&self, val: &TableElement) -> bool { 728 self.element_type().matches(val) 729 } 730 731 fn funcrefs(&self) -> (&[TaggedFuncRef], bool) { 732 assert_eq!(self.element_type(), TableElementType::Func); 733 match self { 734 Self::Dynamic(DynamicTable::Func(DynamicFuncTable { 735 elements, 736 lazy_init, 737 .. 738 })) => ( 739 unsafe { slice::from_raw_parts(elements.as_ptr().cast(), elements.len()) }, 740 *lazy_init, 741 ), 742 Self::Static(StaticTable::Func(StaticFuncTable { 743 data, 744 size, 745 lazy_init, 746 })) => ( 747 unsafe { 748 slice::from_raw_parts(data.as_ptr().cast(), usize::try_from(*size).unwrap()) 749 }, 750 *lazy_init, 751 ), 752 _ => unreachable!(), 753 } 754 } 755 756 fn funcrefs_mut(&mut self) -> (&mut [TaggedFuncRef], bool) { 757 assert_eq!(self.element_type(), TableElementType::Func); 758 match self { 759 Self::Dynamic(DynamicTable::Func(DynamicFuncTable { 760 elements, 761 lazy_init, 762 .. 763 })) => ( 764 unsafe { slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len()) }, 765 *lazy_init, 766 ), 767 Self::Static(StaticTable::Func(StaticFuncTable { 768 data, 769 size, 770 lazy_init, 771 })) => ( 772 unsafe { 773 slice::from_raw_parts_mut(data.as_ptr().cast(), usize::try_from(*size).unwrap()) 774 }, 775 *lazy_init, 776 ), 777 _ => unreachable!(), 778 } 779 } 780 781 fn gc_refs(&self) -> &[Option<VMGcRef>] { 782 assert_eq!(self.element_type(), TableElementType::GcRef); 783 match self { 784 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements, 785 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe { 786 &data.as_non_null().as_ref()[..usize::try_from(*size).unwrap()] 787 }, 788 _ => unreachable!(), 789 } 790 } 791 792 /// Get this table's GC references as a slice. 793 /// 794 /// Panics if this is not a table of GC references. 795 pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] { 796 assert_eq!(self.element_type(), TableElementType::GcRef); 797 match self { 798 Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements, 799 Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe { 800 &mut data.as_non_null().as_mut()[..usize::try_from(*size).unwrap()] 801 }, 802 _ => unreachable!(), 803 } 804 } 805 806 fn copy_elements( 807 gc_store: &mut GcStore, 808 dst_table: &mut Self, 809 src_table: &Self, 810 dst_range: Range<usize>, 811 src_range: Range<usize>, 812 ) { 813 // This can only be used when copying between different tables 814 debug_assert!(!ptr::eq(dst_table, src_table)); 815 816 let ty = dst_table.element_type(); 817 818 match ty { 819 TableElementType::Func => { 820 // `funcref` are `Copy`, so just do a mempcy 821 let (dst_funcrefs, _lazy_init) = dst_table.funcrefs_mut(); 822 let (src_funcrefs, _lazy_init) = src_table.funcrefs(); 823 dst_funcrefs[dst_range].copy_from_slice(&src_funcrefs[src_range]); 824 } 825 TableElementType::GcRef => { 826 assert_eq!( 827 dst_range.end - dst_range.start, 828 src_range.end - src_range.start 829 ); 830 assert!(dst_range.end <= dst_table.gc_refs().len()); 831 assert!(src_range.end <= src_table.gc_refs().len()); 832 for (dst, src) in dst_range.zip(src_range) { 833 gc_store.write_gc_ref( 834 &mut dst_table.gc_refs_mut()[dst], 835 src_table.gc_refs()[src].as_ref(), 836 ); 837 } 838 } 839 } 840 } 841 842 fn copy_elements_within( 843 &mut self, 844 gc_store: &mut GcStore, 845 dst_range: Range<usize>, 846 src_range: Range<usize>, 847 ) { 848 assert_eq!( 849 dst_range.end - dst_range.start, 850 src_range.end - src_range.start 851 ); 852 853 // This is a no-op. 854 if src_range.start == dst_range.start { 855 return; 856 } 857 858 let ty = self.element_type(); 859 match ty { 860 TableElementType::Func => { 861 // `funcref` are `Copy`, so just do a memmove 862 let (funcrefs, _lazy_init) = self.funcrefs_mut(); 863 funcrefs.copy_within(src_range, dst_range.start); 864 } 865 TableElementType::GcRef => { 866 // We need to clone each `externref` while handling overlapping 867 // ranges 868 let elements = self.gc_refs_mut(); 869 if dst_range.start < src_range.start { 870 for (d, s) in dst_range.zip(src_range) { 871 let (ds, ss) = elements.split_at_mut(s); 872 let dst = &mut ds[d]; 873 let src = ss[0].as_ref(); 874 gc_store.write_gc_ref(dst, src); 875 } 876 } else { 877 for (s, d) in src_range.rev().zip(dst_range.rev()) { 878 let (ss, ds) = elements.split_at_mut(d); 879 let dst = &mut ds[0]; 880 let src = ss[s].as_ref(); 881 gc_store.write_gc_ref(dst, src); 882 } 883 } 884 } 885 } 886 } 887 } 888 889 // The default table representation is an empty funcref table that cannot grow. 890 impl Default for Table { 891 fn default() -> Self { 892 Self::from(StaticFuncTable { 893 data: SendSyncPtr::new(NonNull::from(&mut [])), 894 size: 0, 895 lazy_init: false, 896 }) 897 } 898 } 899