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