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