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