1 use crate::runtime::vm::TableElement; 2 use crate::store::{AutoAssertNoGc, StoreOpaque}; 3 use crate::{prelude::*, StructRef}; 4 use crate::{ 5 AnyRef, AsContext, AsContextMut, ExternRef, Func, HeapType, RefType, Rooted, RootedGcRefImpl, 6 ValType, V128, 7 }; 8 use core::ptr; 9 10 pub use crate::runtime::vm::ValRaw; 11 12 /// Possible runtime values that a WebAssembly module can either consume or 13 /// produce. 14 /// 15 /// Note that we inline the `enum Ref { ... }` variants into `enum Val { ... }` 16 /// here as a size optimization. 17 #[derive(Debug, Clone, Copy)] 18 pub enum Val { 19 // NB: the ordering here is intended to match the ordering in 20 // `ValType` to improve codegen when learning the type of a value. 21 // 22 /// A 32-bit integer. 23 I32(i32), 24 25 /// A 64-bit integer. 26 I64(i64), 27 28 /// A 32-bit float. 29 /// 30 /// Note that the raw bits of the float are stored here, and you can use 31 /// `f32::from_bits` to create an `f32` value. 32 F32(u32), 33 34 /// A 64-bit float. 35 /// 36 /// Note that the raw bits of the float are stored here, and you can use 37 /// `f64::from_bits` to create an `f64` value. 38 F64(u64), 39 40 /// A 128-bit number. 41 V128(V128), 42 43 /// A function reference. 44 FuncRef(Option<Func>), 45 46 /// An external reference. 47 ExternRef(Option<Rooted<ExternRef>>), 48 49 /// An internal reference. 50 AnyRef(Option<Rooted<AnyRef>>), 51 } 52 53 macro_rules! accessors { 54 ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($( 55 /// Attempt to access the underlying value of this `Val`, returning 56 /// `None` if it is not the correct type. 57 #[inline] 58 pub fn $get(&self) -> Option<$ty> { 59 if let Val::$variant($bind) = self { 60 Some($cvt) 61 } else { 62 None 63 } 64 } 65 66 /// Returns the underlying value of this `Val`, panicking if it's the 67 /// wrong type. 68 /// 69 /// # Panics 70 /// 71 /// Panics if `self` is not of the right type. 72 #[inline] 73 pub fn $unwrap(&self) -> $ty { 74 self.$get().expect(concat!("expected ", stringify!($ty))) 75 } 76 )*) 77 } 78 79 impl Val { 80 /// Returns the null reference for the given heap type. 81 #[inline] 82 pub fn null_ref(heap_type: &HeapType) -> Val { 83 Ref::null(&heap_type).into() 84 } 85 86 /// Returns the null function reference value. 87 /// 88 /// The return value has type `(ref null nofunc)` aka `nullfuncref` and is a 89 /// subtype of all function references. 90 #[inline] 91 pub const fn null_func_ref() -> Val { 92 Val::FuncRef(None) 93 } 94 95 /// Returns the null function reference value. 96 /// 97 /// The return value has type `(ref null extern)` aka `nullexternref` and is 98 /// a subtype of all external references. 99 #[inline] 100 pub const fn null_extern_ref() -> Val { 101 Val::ExternRef(None) 102 } 103 104 /// Returns the null function reference value. 105 /// 106 /// The return value has type `(ref null any)` aka `nullref` and is a 107 /// subtype of all internal references. 108 #[inline] 109 pub const fn null_any_ref() -> Val { 110 Val::AnyRef(None) 111 } 112 113 /// Returns the corresponding [`ValType`] for this `Val`. 114 /// 115 /// # Errors 116 /// 117 /// Returns an error if this value is a GC reference that has since been 118 /// unrooted. 119 /// 120 /// # Panics 121 /// 122 /// Panics if this value is associated with a different store. 123 #[inline] 124 pub fn ty(&self, store: impl AsContext) -> Result<ValType> { 125 self.load_ty(&store.as_context().0) 126 } 127 128 #[inline] 129 pub(crate) fn load_ty(&self, store: &StoreOpaque) -> Result<ValType> { 130 Ok(match self { 131 Val::I32(_) => ValType::I32, 132 Val::I64(_) => ValType::I64, 133 Val::F32(_) => ValType::F32, 134 Val::F64(_) => ValType::F64, 135 Val::V128(_) => ValType::V128, 136 Val::ExternRef(Some(_)) => ValType::EXTERNREF, 137 Val::ExternRef(None) => ValType::NULLFUNCREF, 138 Val::FuncRef(None) => ValType::NULLFUNCREF, 139 Val::FuncRef(Some(f)) => ValType::Ref(RefType::new( 140 false, 141 HeapType::ConcreteFunc(f.load_ty(store)), 142 )), 143 Val::AnyRef(None) => ValType::NULLREF, 144 Val::AnyRef(Some(a)) => ValType::Ref(RefType::new(false, a._ty(store)?)), 145 }) 146 } 147 148 /// Does this value match the given type? 149 /// 150 /// Returns an error is an underlying `Rooted` has been unrooted. 151 /// 152 /// # Panics 153 /// 154 /// Panics if this value is not associated with the given store. 155 pub fn matches_ty(&self, store: impl AsContext, ty: &ValType) -> Result<bool> { 156 self._matches_ty(&store.as_context().0, ty) 157 } 158 159 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<bool> { 160 assert!(self.comes_from_same_store(store)); 161 assert!(ty.comes_from_same_engine(store.engine())); 162 Ok(match (self, ty) { 163 (Val::I32(_), ValType::I32) 164 | (Val::I64(_), ValType::I64) 165 | (Val::F32(_), ValType::F32) 166 | (Val::F64(_), ValType::F64) 167 | (Val::V128(_), ValType::V128) => true, 168 169 (Val::FuncRef(f), ValType::Ref(ref_ty)) => Ref::from(*f)._matches_ty(store, ref_ty)?, 170 (Val::ExternRef(e), ValType::Ref(ref_ty)) => { 171 Ref::from(*e)._matches_ty(store, ref_ty)? 172 } 173 (Val::AnyRef(a), ValType::Ref(ref_ty)) => Ref::from(*a)._matches_ty(store, ref_ty)?, 174 175 (Val::I32(_), _) 176 | (Val::I64(_), _) 177 | (Val::F32(_), _) 178 | (Val::F64(_), _) 179 | (Val::V128(_), _) 180 | (Val::FuncRef(_), _) 181 | (Val::ExternRef(_), _) 182 | (Val::AnyRef(_), _) => false, 183 }) 184 } 185 186 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &ValType) -> Result<()> { 187 if !self.comes_from_same_store(store) { 188 bail!("value used with wrong store") 189 } 190 if !ty.comes_from_same_engine(store.engine()) { 191 bail!("type used with wrong engine") 192 } 193 if self._matches_ty(store, ty)? { 194 Ok(()) 195 } else { 196 let actual_ty = self.load_ty(store)?; 197 bail!("type mismatch: expected {ty}, found {actual_ty}") 198 } 199 } 200 201 /// Convenience method to convert this [`Val`] into a [`ValRaw`]. 202 /// 203 /// Returns an error if this value is a GC reference and the GC reference 204 /// has been unrooted. 205 /// 206 /// # Unsafety 207 /// 208 /// This method is unsafe for the reasons that [`ExternRef::to_raw`] and 209 /// [`Func::to_raw`] are unsafe. 210 pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<ValRaw> { 211 match self { 212 Val::I32(i) => Ok(ValRaw::i32(*i)), 213 Val::I64(i) => Ok(ValRaw::i64(*i)), 214 Val::F32(u) => Ok(ValRaw::f32(*u)), 215 Val::F64(u) => Ok(ValRaw::f64(*u)), 216 Val::V128(b) => Ok(ValRaw::v128(b.as_u128())), 217 Val::ExternRef(e) => Ok(ValRaw::externref(match e { 218 None => 0, 219 Some(e) => e.to_raw(store)?, 220 })), 221 Val::AnyRef(e) => Ok(ValRaw::anyref(match e { 222 None => 0, 223 Some(e) => e.to_raw(store)?, 224 })), 225 Val::FuncRef(f) => Ok(ValRaw::funcref(match f { 226 Some(f) => f.to_raw(store), 227 None => ptr::null_mut(), 228 })), 229 } 230 } 231 232 /// Convenience method to convert a [`ValRaw`] into a [`Val`]. 233 /// 234 /// # Unsafety 235 /// 236 /// This method is unsafe for the reasons that [`ExternRef::from_raw`] and 237 /// [`Func::from_raw`] are unsafe. Additionally there's no guarantee 238 /// otherwise that `raw` should have the type `ty` specified. 239 pub unsafe fn from_raw(store: impl AsContextMut, raw: ValRaw, ty: ValType) -> Val { 240 match ty { 241 ValType::I32 => Val::I32(raw.get_i32()), 242 ValType::I64 => Val::I64(raw.get_i64()), 243 ValType::F32 => Val::F32(raw.get_f32()), 244 ValType::F64 => Val::F64(raw.get_f64()), 245 ValType::V128 => Val::V128(raw.get_v128().into()), 246 ValType::Ref(ref_ty) => { 247 let ref_ = match ref_ty.heap_type() { 248 HeapType::Func | HeapType::ConcreteFunc(_) => { 249 Func::from_raw(store, raw.get_funcref()).into() 250 } 251 252 HeapType::NoFunc => Ref::Func(None), 253 254 HeapType::Extern => ExternRef::from_raw(store, raw.get_externref()).into(), 255 256 HeapType::NoExtern => Ref::Extern(None), 257 258 HeapType::Any 259 | HeapType::Eq 260 | HeapType::I31 261 | HeapType::Array 262 | HeapType::ConcreteArray(_) 263 | HeapType::Struct 264 | HeapType::ConcreteStruct(_) => { 265 AnyRef::from_raw(store, raw.get_anyref()).into() 266 } 267 268 HeapType::None => Ref::Any(None), 269 }; 270 assert!( 271 ref_ty.is_nullable() || !ref_.is_null(), 272 "if the type is not nullable, we shouldn't get null; got \ 273 type = {ref_ty}, ref = {ref_:?}" 274 ); 275 ref_.into() 276 } 277 } 278 } 279 280 accessors! { 281 e 282 (I32(i32) i32 unwrap_i32 *e) 283 (I64(i64) i64 unwrap_i64 *e) 284 (F32(f32) f32 unwrap_f32 f32::from_bits(*e)) 285 (F64(f64) f64 unwrap_f64 f64::from_bits(*e)) 286 (FuncRef(Option<&Func>) func_ref unwrap_func_ref e.as_ref()) 287 (ExternRef(Option<&Rooted<ExternRef>>) extern_ref unwrap_extern_ref e.as_ref()) 288 (AnyRef(Option<&Rooted<AnyRef>>) any_ref unwrap_any_ref e.as_ref()) 289 (V128(V128) v128 unwrap_v128 *e) 290 } 291 292 /// Get this value's underlying reference, if any. 293 #[inline] 294 pub fn ref_(self) -> Option<Ref> { 295 match self { 296 Val::FuncRef(f) => Some(Ref::Func(f)), 297 Val::ExternRef(e) => Some(Ref::Extern(e)), 298 Val::AnyRef(a) => Some(Ref::Any(a)), 299 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => None, 300 } 301 } 302 303 /// Attempt to access the underlying `externref` value of this `Val`. 304 /// 305 /// If this is not an `externref`, then `None` is returned. 306 /// 307 /// If this is a null `externref`, then `Some(None)` is returned. 308 /// 309 /// If this is a non-null `externref`, then `Some(Some(..))` is returned. 310 #[inline] 311 pub fn externref(&self) -> Option<Option<&Rooted<ExternRef>>> { 312 match self { 313 Val::ExternRef(None) => Some(None), 314 Val::ExternRef(Some(e)) => Some(Some(e)), 315 _ => None, 316 } 317 } 318 319 /// Returns the underlying `externref` value of this `Val`, panicking if it's the 320 /// wrong type. 321 /// 322 /// If this is a null `externref`, then `None` is returned. 323 /// 324 /// If this is a non-null `externref`, then `Some(..)` is returned. 325 /// 326 /// # Panics 327 /// 328 /// Panics if `self` is not a (nullable) `externref`. 329 #[inline] 330 pub fn unwrap_externref(&self) -> Option<&Rooted<ExternRef>> { 331 self.externref().expect("expected externref") 332 } 333 334 /// Attempt to access the underlying `anyref` value of this `Val`. 335 /// 336 /// If this is not an `anyref`, then `None` is returned. 337 /// 338 /// If this is a null `anyref`, then `Some(None)` is returned. 339 /// 340 /// If this is a non-null `anyref`, then `Some(Some(..))` is returned. 341 #[inline] 342 pub fn anyref(&self) -> Option<Option<&Rooted<AnyRef>>> { 343 match self { 344 Val::AnyRef(None) => Some(None), 345 Val::AnyRef(Some(e)) => Some(Some(e)), 346 _ => None, 347 } 348 } 349 350 /// Returns the underlying `anyref` value of this `Val`, panicking if it's the 351 /// wrong type. 352 /// 353 /// If this is a null `anyref`, then `None` is returned. 354 /// 355 /// If this is a non-null `anyref`, then `Some(..)` is returned. 356 /// 357 /// # Panics 358 /// 359 /// Panics if `self` is not a (nullable) `anyref`. 360 #[inline] 361 pub fn unwrap_anyref(&self) -> Option<&Rooted<AnyRef>> { 362 self.anyref().expect("expected anyref") 363 } 364 365 /// Attempt to access the underlying `funcref` value of this `Val`. 366 /// 367 /// If this is not an `funcref`, then `None` is returned. 368 /// 369 /// If this is a null `funcref`, then `Some(None)` is returned. 370 /// 371 /// If this is a non-null `funcref`, then `Some(Some(..))` is returned. 372 #[inline] 373 pub fn funcref(&self) -> Option<Option<&Func>> { 374 match self { 375 Val::FuncRef(None) => Some(None), 376 Val::FuncRef(Some(f)) => Some(Some(f)), 377 _ => None, 378 } 379 } 380 381 /// Returns the underlying `funcref` value of this `Val`, panicking if it's the 382 /// wrong type. 383 /// 384 /// If this is a null `funcref`, then `None` is returned. 385 /// 386 /// If this is a non-null `funcref`, then `Some(..)` is returned. 387 /// 388 /// # Panics 389 /// 390 /// Panics if `self` is not a (nullable) `funcref`. 391 #[inline] 392 pub fn unwrap_funcref(&self) -> Option<&Func> { 393 self.funcref().expect("expected funcref") 394 } 395 396 #[inline] 397 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 398 match self { 399 Val::FuncRef(Some(f)) => f.comes_from_same_store(store), 400 Val::FuncRef(None) => true, 401 402 Val::ExternRef(Some(x)) => x.comes_from_same_store(store), 403 Val::ExternRef(None) => true, 404 405 Val::AnyRef(Some(a)) => a.comes_from_same_store(store), 406 Val::AnyRef(None) => true, 407 408 // Integers, floats, and vectors have no association with any 409 // particular store, so they're always considered as "yes I came 410 // from that store", 411 Val::I32(_) | Val::I64(_) | Val::F32(_) | Val::F64(_) | Val::V128(_) => true, 412 } 413 } 414 } 415 416 impl From<i32> for Val { 417 #[inline] 418 fn from(val: i32) -> Val { 419 Val::I32(val) 420 } 421 } 422 423 impl From<i64> for Val { 424 #[inline] 425 fn from(val: i64) -> Val { 426 Val::I64(val) 427 } 428 } 429 430 impl From<f32> for Val { 431 #[inline] 432 fn from(val: f32) -> Val { 433 Val::F32(val.to_bits()) 434 } 435 } 436 437 impl From<f64> for Val { 438 #[inline] 439 fn from(val: f64) -> Val { 440 Val::F64(val.to_bits()) 441 } 442 } 443 444 impl From<Ref> for Val { 445 #[inline] 446 fn from(val: Ref) -> Val { 447 match val { 448 Ref::Extern(e) => Val::ExternRef(e), 449 Ref::Func(f) => Val::FuncRef(f), 450 Ref::Any(a) => Val::AnyRef(a), 451 } 452 } 453 } 454 455 impl From<Rooted<ExternRef>> for Val { 456 #[inline] 457 fn from(val: Rooted<ExternRef>) -> Val { 458 Val::ExternRef(Some(val)) 459 } 460 } 461 462 impl From<Option<Rooted<ExternRef>>> for Val { 463 #[inline] 464 fn from(val: Option<Rooted<ExternRef>>) -> Val { 465 Val::ExternRef(val) 466 } 467 } 468 469 impl From<Rooted<AnyRef>> for Val { 470 #[inline] 471 fn from(val: Rooted<AnyRef>) -> Val { 472 Val::AnyRef(Some(val)) 473 } 474 } 475 476 impl From<Option<Rooted<AnyRef>>> for Val { 477 #[inline] 478 fn from(val: Option<Rooted<AnyRef>>) -> Val { 479 Val::AnyRef(val) 480 } 481 } 482 483 impl From<Rooted<StructRef>> for Val { 484 #[inline] 485 fn from(val: Rooted<StructRef>) -> Val { 486 Val::AnyRef(Some(val.unchecked_cast())) 487 } 488 } 489 490 impl From<Option<Rooted<StructRef>>> for Val { 491 #[inline] 492 fn from(val: Option<Rooted<StructRef>>) -> Val { 493 Val::AnyRef(val.map(|s| s.unchecked_cast())) 494 } 495 } 496 497 impl From<Func> for Val { 498 #[inline] 499 fn from(val: Func) -> Val { 500 Val::FuncRef(Some(val)) 501 } 502 } 503 504 impl From<Option<Func>> for Val { 505 #[inline] 506 fn from(val: Option<Func>) -> Val { 507 Val::FuncRef(val) 508 } 509 } 510 511 impl From<u128> for Val { 512 #[inline] 513 fn from(val: u128) -> Val { 514 Val::V128(val.into()) 515 } 516 } 517 518 impl From<V128> for Val { 519 #[inline] 520 fn from(val: V128) -> Val { 521 Val::V128(val) 522 } 523 } 524 525 /// A reference. 526 /// 527 /// References come in three broad flavors: 528 /// 529 /// 1. Function references. These are references to a function that can be 530 /// invoked. 531 /// 532 /// 2. External references. These are references to data that is external 533 /// and opaque to the Wasm guest, provided by the host. 534 /// 535 /// 3. Internal references. These are references to allocations inside the 536 /// Wasm's heap, such as structs and arrays. These are part of the GC 537 /// proposal, and not yet implemented in Wasmtime. 538 /// 539 /// At the Wasm level, there are nullable and non-nullable variants of each type 540 /// of reference. Both variants are represented with `Ref` at the Wasmtime API 541 /// level. For example, values of both `(ref extern)` and `(ref null extern)` 542 /// types will be represented as `Ref::Extern(Option<ExternRef>)` in the 543 /// Wasmtime API. Nullable references are represented as `Option<Ref>` where 544 /// null references are represented as `None`. Wasm can construct null 545 /// references via the `ref.null <heap-type>` instruction. 546 /// 547 /// References are non-forgable: Wasm cannot create invalid references, for 548 /// example, by claiming that the integer `0xbad1bad2` is actually a reference. 549 #[derive(Debug, Clone)] 550 pub enum Ref { 551 // NB: We have a variant for each of the type hierarchies defined in Wasm, 552 // and push the `Option` that provides nullability into each variant. This 553 // allows us to get the most-precise type of any reference value, whether it 554 // is null or not, without any additional metadata. 555 // 556 // Consider if we instead had the nullability inside `Val::Ref` and each of 557 // the `Ref` variants did not have an `Option`: 558 // 559 // enum Val { 560 // Ref(Option<Ref>), 561 // // Etc... 562 // } 563 // enum Ref { 564 // Func(Func), 565 // External(ExternRef), 566 // // Etc... 567 // } 568 // 569 // In this scenario, what type would we return from `Val::ty` for 570 // `Val::Ref(None)`? Because Wasm has multiple separate type hierarchies, 571 // there is no single common bottom type for all the different kinds of 572 // references. So in this scenario, `Val::Ref(None)` doesn't have enough 573 // information to reconstruct the value's type. That's a problem for us 574 // because we need to get a value's type at various times all over the code 575 // base. 576 // 577 /// A first-class reference to a WebAssembly function. 578 /// 579 /// The host, or the Wasm guest, can invoke this function. 580 /// 581 /// The host can create function references via [`Func::new`] or 582 /// [`Func::wrap`]. 583 /// 584 /// The Wasm guest can create non-null function references via the 585 /// `ref.func` instruction, or null references via the `ref.null func` 586 /// instruction. 587 Func(Option<Func>), 588 589 /// A reference to an value outside of the Wasm heap. 590 /// 591 /// These references are opaque to the Wasm itself. Wasm can't create 592 /// non-null external references, nor do anything with them accept pass them 593 /// around as function arguments and returns and place them into globals and 594 /// tables. 595 /// 596 /// Wasm can create null external references via the `ref.null extern` 597 /// instruction. 598 Extern(Option<Rooted<ExternRef>>), 599 600 /// An internal reference. 601 /// 602 /// The `AnyRef` type represents WebAssembly `anyref` values. These can be 603 /// references to `struct`s and `array`s or inline/unboxed 31-bit 604 /// integers. 605 /// 606 /// Unlike `externref`, Wasm guests can directly allocate `anyref`s, and 607 /// does not need to rely on the host to do that. 608 Any(Option<Rooted<AnyRef>>), 609 } 610 611 impl From<Func> for Ref { 612 #[inline] 613 fn from(f: Func) -> Ref { 614 Ref::Func(Some(f)) 615 } 616 } 617 618 impl From<Option<Func>> for Ref { 619 #[inline] 620 fn from(f: Option<Func>) -> Ref { 621 Ref::Func(f) 622 } 623 } 624 625 impl From<Rooted<ExternRef>> for Ref { 626 #[inline] 627 fn from(e: Rooted<ExternRef>) -> Ref { 628 Ref::Extern(Some(e)) 629 } 630 } 631 632 impl From<Option<Rooted<ExternRef>>> for Ref { 633 #[inline] 634 fn from(e: Option<Rooted<ExternRef>>) -> Ref { 635 Ref::Extern(e) 636 } 637 } 638 639 impl From<Rooted<AnyRef>> for Ref { 640 #[inline] 641 fn from(e: Rooted<AnyRef>) -> Ref { 642 Ref::Any(Some(e)) 643 } 644 } 645 646 impl From<Option<Rooted<AnyRef>>> for Ref { 647 #[inline] 648 fn from(e: Option<Rooted<AnyRef>>) -> Ref { 649 Ref::Any(e) 650 } 651 } 652 653 impl From<Rooted<StructRef>> for Ref { 654 #[inline] 655 fn from(e: Rooted<StructRef>) -> Ref { 656 Ref::Any(Some(e.unchecked_cast::<AnyRef>())) 657 } 658 } 659 660 impl From<Option<Rooted<StructRef>>> for Ref { 661 #[inline] 662 fn from(e: Option<Rooted<StructRef>>) -> Ref { 663 Ref::Any(e.map(|e| e.unchecked_cast::<AnyRef>())) 664 } 665 } 666 667 impl Ref { 668 /// Create a null reference to the given heap type. 669 #[inline] 670 pub fn null(heap_type: &HeapType) -> Self { 671 match heap_type.top() { 672 HeapType::Any => Ref::Any(None), 673 HeapType::Extern => Ref::Extern(None), 674 HeapType::Func => Ref::Func(None), 675 ty => unreachable!("not a heap type: {ty:?}"), 676 } 677 } 678 679 /// Is this a null reference? 680 #[inline] 681 pub fn is_null(&self) -> bool { 682 match self { 683 Ref::Any(None) | Ref::Extern(None) | Ref::Func(None) => true, 684 Ref::Any(Some(_)) | Ref::Extern(Some(_)) | Ref::Func(Some(_)) => false, 685 } 686 } 687 688 /// Is this a non-null reference? 689 #[inline] 690 pub fn is_non_null(&self) -> bool { 691 !self.is_null() 692 } 693 694 /// Is this an `extern` reference? 695 #[inline] 696 pub fn is_extern(&self) -> bool { 697 matches!(self, Ref::Extern(_)) 698 } 699 700 /// Get the underlying `extern` reference, if any. 701 /// 702 /// Returns `None` if this `Ref` is not an `extern` reference, eg it is a 703 /// `func` reference. 704 /// 705 /// Returns `Some(None)` if this `Ref` is a null `extern` reference. 706 /// 707 /// Returns `Some(Some(_))` if this `Ref` is a non-null `extern` reference. 708 #[inline] 709 pub fn as_extern(&self) -> Option<Option<&Rooted<ExternRef>>> { 710 match self { 711 Ref::Extern(e) => Some(e.as_ref()), 712 _ => None, 713 } 714 } 715 716 /// Get the underlying `extern` reference, panicking if this is a different 717 /// kind of reference. 718 /// 719 /// Returns `None` if this `Ref` is a null `extern` reference. 720 /// 721 /// Returns `Some(_)` if this `Ref` is a non-null `extern` reference. 722 #[inline] 723 pub fn unwrap_extern(&self) -> Option<&Rooted<ExternRef>> { 724 self.as_extern() 725 .expect("Ref::unwrap_extern on non-extern reference") 726 } 727 728 /// Is this an `any` reference? 729 #[inline] 730 pub fn is_any(&self) -> bool { 731 matches!(self, Ref::Any(_)) 732 } 733 734 /// Get the underlying `any` reference, if any. 735 /// 736 /// Returns `None` if this `Ref` is not an `any` reference, eg it is a 737 /// `func` reference. 738 /// 739 /// Returns `Some(None)` if this `Ref` is a null `any` reference. 740 /// 741 /// Returns `Some(Some(_))` if this `Ref` is a non-null `any` reference. 742 #[inline] 743 pub fn as_any(&self) -> Option<Option<&Rooted<AnyRef>>> { 744 match self { 745 Ref::Any(e) => Some(e.as_ref()), 746 _ => None, 747 } 748 } 749 750 /// Get the underlying `any` reference, panicking if this is a different 751 /// kind of reference. 752 /// 753 /// Returns `None` if this `Ref` is a null `any` reference. 754 /// 755 /// Returns `Some(_)` if this `Ref` is a non-null `any` reference. 756 #[inline] 757 pub fn unwrap_any(&self) -> Option<&Rooted<AnyRef>> { 758 self.as_any().expect("Ref::unwrap_any on non-any reference") 759 } 760 761 /// Is this a `func` reference? 762 #[inline] 763 pub fn is_func(&self) -> bool { 764 matches!(self, Ref::Func(_)) 765 } 766 767 /// Get the underlying `func` reference, if any. 768 /// 769 /// Returns `None` if this `Ref` is not an `func` reference, eg it is an 770 /// `extern` reference. 771 /// 772 /// Returns `Some(None)` if this `Ref` is a null `func` reference. 773 /// 774 /// Returns `Some(Some(_))` if this `Ref` is a non-null `func` reference. 775 #[inline] 776 pub fn as_func(&self) -> Option<Option<&Func>> { 777 match self { 778 Ref::Func(f) => Some(f.as_ref()), 779 _ => None, 780 } 781 } 782 783 /// Get the underlying `func` reference, panicking if this is a different 784 /// kind of reference. 785 /// 786 /// Returns `None` if this `Ref` is a null `func` reference. 787 /// 788 /// Returns `Some(_)` if this `Ref` is a non-null `func` reference. 789 #[inline] 790 pub fn unwrap_func(&self) -> Option<&Func> { 791 self.as_func() 792 .expect("Ref::unwrap_func on non-func reference") 793 } 794 795 /// Get the type of this reference. 796 /// 797 /// # Errors 798 /// 799 /// Return an error if this reference has been unrooted. 800 /// 801 /// # Panics 802 /// 803 /// Panics if this reference is associated with a different store. 804 pub fn ty(&self, store: impl AsContext) -> Result<RefType> { 805 self.load_ty(&store.as_context().0) 806 } 807 808 pub(crate) fn load_ty(&self, store: &StoreOpaque) -> Result<RefType> { 809 assert!(self.comes_from_same_store(store)); 810 Ok(RefType::new( 811 self.is_null(), 812 // NB: We choose the most-specific heap type we can here and let 813 // subtyping do its thing if callers are matching against a 814 // `HeapType::Func`. 815 match self { 816 Ref::Extern(None) => HeapType::NoExtern, 817 Ref::Extern(Some(_)) => HeapType::Extern, 818 819 Ref::Func(None) => HeapType::NoFunc, 820 Ref::Func(Some(f)) => HeapType::ConcreteFunc(f.load_ty(store)), 821 822 Ref::Any(None) => HeapType::None, 823 Ref::Any(Some(a)) => a._ty(store)?, 824 }, 825 )) 826 } 827 828 /// Does this reference value match the given type? 829 /// 830 /// Returns an error if the underlying `Rooted` has been unrooted. 831 /// 832 /// # Panics 833 /// 834 /// Panics if this reference is not associated with the given store. 835 pub fn matches_ty(&self, store: impl AsContext, ty: &RefType) -> Result<bool> { 836 self._matches_ty(&store.as_context().0, ty) 837 } 838 839 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<bool> { 840 assert!(self.comes_from_same_store(store)); 841 assert!(ty.comes_from_same_engine(store.engine())); 842 if self.is_null() && !ty.is_nullable() { 843 return Ok(false); 844 } 845 Ok(match (self, ty.heap_type()) { 846 (Ref::Extern(_), HeapType::Extern) => true, 847 (Ref::Extern(_), _) => false, 848 849 (Ref::Func(_), HeapType::Func) => true, 850 (Ref::Func(None), HeapType::NoFunc | HeapType::ConcreteFunc(_)) => true, 851 (Ref::Func(Some(f)), HeapType::ConcreteFunc(func_ty)) => f._matches_ty(store, func_ty), 852 (Ref::Func(_), _) => false, 853 854 (Ref::Any(_), HeapType::Any) => true, 855 (Ref::Any(Some(a)), HeapType::I31) => a._is_i31(store)?, 856 (Ref::Any(Some(a)), HeapType::Struct) => a._is_struct(store)?, 857 (Ref::Any(Some(a)), HeapType::ConcreteStruct(ty)) => match a._as_struct(store)? { 858 None => false, 859 Some(s) => s._matches_ty(store, ty)?, 860 }, 861 (Ref::Any(Some(_)), HeapType::Eq) => todo!("eqref"), 862 (Ref::Any(Some(_)), HeapType::Array) => todo!("wasm GC arrays"), 863 (Ref::Any(Some(_)), HeapType::ConcreteArray(_)) => todo!("wasm GC arrays"), 864 ( 865 Ref::Any(None), 866 HeapType::None 867 | HeapType::I31 868 | HeapType::ConcreteStruct(_) 869 | HeapType::Struct 870 | HeapType::ConcreteArray(_) 871 | HeapType::Array, 872 ) => true, 873 (Ref::Any(_), _) => false, 874 }) 875 } 876 877 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<()> { 878 if !self.comes_from_same_store(store) { 879 bail!("reference used with wrong store") 880 } 881 if !ty.comes_from_same_engine(store.engine()) { 882 bail!("type used with wrong engine") 883 } 884 if self._matches_ty(store, ty)? { 885 Ok(()) 886 } else { 887 let actual_ty = self.load_ty(store)?; 888 bail!("type mismatch: expected {ty}, found {actual_ty}") 889 } 890 } 891 892 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 893 match self { 894 Ref::Func(Some(f)) => f.comes_from_same_store(store), 895 Ref::Func(None) => true, 896 Ref::Extern(Some(x)) => x.comes_from_same_store(store), 897 Ref::Extern(None) => true, 898 Ref::Any(Some(a)) => a.comes_from_same_store(store), 899 Ref::Any(None) => true, 900 } 901 } 902 903 pub(crate) fn into_table_element( 904 self, 905 store: &mut StoreOpaque, 906 ty: &RefType, 907 ) -> Result<TableElement> { 908 let mut store = AutoAssertNoGc::new(store); 909 self.ensure_matches_ty(&store, &ty) 910 .context("type mismatch: value does not match table element type")?; 911 912 match (self, ty.heap_type().top()) { 913 (Ref::Func(None), HeapType::Func) => { 914 assert!(ty.is_nullable()); 915 Ok(TableElement::FuncRef(ptr::null_mut())) 916 } 917 (Ref::Func(Some(f)), HeapType::Func) => { 918 debug_assert!( 919 f.comes_from_same_store(&store), 920 "checked in `ensure_matches_ty`" 921 ); 922 Ok(TableElement::FuncRef(f.vm_func_ref(&mut store).as_ptr())) 923 } 924 925 (Ref::Extern(e), HeapType::Extern) => match e { 926 None => { 927 assert!(ty.is_nullable()); 928 Ok(TableElement::GcRef(None)) 929 } 930 Some(e) => { 931 let gc_ref = e.try_clone_gc_ref(&mut store)?; 932 Ok(TableElement::GcRef(Some(gc_ref))) 933 } 934 }, 935 936 (Ref::Any(a), HeapType::Any) => match a { 937 None => { 938 assert!(ty.is_nullable()); 939 Ok(TableElement::GcRef(None)) 940 } 941 Some(a) => { 942 let gc_ref = a.try_clone_gc_ref(&mut store)?; 943 Ok(TableElement::GcRef(Some(gc_ref))) 944 } 945 }, 946 947 _ => unreachable!("checked that the value matches the type above"), 948 } 949 } 950 } 951 952 #[cfg(test)] 953 mod tests { 954 use crate::*; 955 956 #[test] 957 fn size_of_val() { 958 // Try to keep tabs on the size of `Val` and make sure we don't grow its 959 // size. 960 assert_eq!( 961 std::mem::size_of::<Val>(), 962 if cfg!(any( 963 target_arch = "x86_64", 964 target_arch = "aarch64", 965 target_arch = "riscv64", 966 target_arch = "s390x" 967 )) { 968 24 969 } else { 970 panic!("unsupported architecture") 971 } 972 ); 973 } 974 975 #[test] 976 fn size_of_ref() { 977 // Try to keep tabs on the size of `Ref` and make sure we don't grow its 978 // size. 979 assert_eq!(std::mem::size_of::<Ref>(), 24); 980 } 981 982 #[test] 983 #[should_panic] 984 fn val_matches_ty_wrong_engine() { 985 let e1 = Engine::default(); 986 let e2 = Engine::default(); 987 988 let t1 = FuncType::new(&e1, None, None); 989 let t2 = FuncType::new(&e2, None, None); 990 991 let mut s1 = Store::new(&e1, ()); 992 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(())); 993 994 // Should panic. 995 let _ = Val::FuncRef(Some(f)).matches_ty( 996 &s1, 997 &ValType::Ref(RefType::new(true, HeapType::ConcreteFunc(t2))), 998 ); 999 } 1000 1001 #[test] 1002 #[should_panic] 1003 fn ref_matches_ty_wrong_engine() { 1004 let e1 = Engine::default(); 1005 let e2 = Engine::default(); 1006 1007 let t1 = FuncType::new(&e1, None, None); 1008 let t2 = FuncType::new(&e2, None, None); 1009 1010 let mut s1 = Store::new(&e1, ()); 1011 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(())); 1012 1013 // Should panic. 1014 let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::ConcreteFunc(t2))); 1015 } 1016 } 1017