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