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