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