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 HeapType::Exn => Ref::Exn(None), 841 ty => unreachable!("not a heap type: {ty:?}"), 842 } 843 } 844 845 /// Is this a null reference? 846 #[inline] 847 pub fn is_null(&self) -> bool { 848 match self { 849 Ref::Any(None) | Ref::Extern(None) | Ref::Func(None) | Ref::Exn(None) => true, 850 Ref::Any(Some(_)) | Ref::Extern(Some(_)) | Ref::Func(Some(_)) | Ref::Exn(Some(_)) => { 851 false 852 } 853 } 854 } 855 856 /// Is this a non-null reference? 857 #[inline] 858 pub fn is_non_null(&self) -> bool { 859 !self.is_null() 860 } 861 862 /// Is this an `extern` reference? 863 #[inline] 864 pub fn is_extern(&self) -> bool { 865 matches!(self, Ref::Extern(_)) 866 } 867 868 /// Get the underlying `extern` reference, if any. 869 /// 870 /// Returns `None` if this `Ref` is not an `extern` reference, eg it is a 871 /// `func` reference. 872 /// 873 /// Returns `Some(None)` if this `Ref` is a null `extern` reference. 874 /// 875 /// Returns `Some(Some(_))` if this `Ref` is a non-null `extern` reference. 876 #[inline] 877 pub fn as_extern(&self) -> Option<Option<&Rooted<ExternRef>>> { 878 match self { 879 Ref::Extern(e) => Some(e.as_ref()), 880 _ => None, 881 } 882 } 883 884 /// Get the underlying `extern` reference, panicking if this is a different 885 /// kind of reference. 886 /// 887 /// Returns `None` if this `Ref` is a null `extern` reference. 888 /// 889 /// Returns `Some(_)` if this `Ref` is a non-null `extern` reference. 890 #[inline] 891 pub fn unwrap_extern(&self) -> Option<&Rooted<ExternRef>> { 892 self.as_extern() 893 .expect("Ref::unwrap_extern on non-extern reference") 894 } 895 896 /// Is this an `any` reference? 897 #[inline] 898 pub fn is_any(&self) -> bool { 899 matches!(self, Ref::Any(_)) 900 } 901 902 /// Get the underlying `any` reference, if any. 903 /// 904 /// Returns `None` if this `Ref` is not an `any` reference, eg it is a 905 /// `func` reference. 906 /// 907 /// Returns `Some(None)` if this `Ref` is a null `any` reference. 908 /// 909 /// Returns `Some(Some(_))` if this `Ref` is a non-null `any` reference. 910 #[inline] 911 pub fn as_any(&self) -> Option<Option<&Rooted<AnyRef>>> { 912 match self { 913 Ref::Any(e) => Some(e.as_ref()), 914 _ => None, 915 } 916 } 917 918 /// Get the underlying `any` reference, panicking if this is a different 919 /// kind of reference. 920 /// 921 /// Returns `None` if this `Ref` is a null `any` reference. 922 /// 923 /// Returns `Some(_)` if this `Ref` is a non-null `any` reference. 924 #[inline] 925 pub fn unwrap_any(&self) -> Option<&Rooted<AnyRef>> { 926 self.as_any().expect("Ref::unwrap_any on non-any reference") 927 } 928 929 /// Is this an `exn` reference? 930 #[inline] 931 pub fn is_exn(&self) -> bool { 932 matches!(self, Ref::Exn(_)) 933 } 934 935 /// Get the underlying `exn` reference, if any. 936 /// 937 /// Returns `None` if this `Ref` is not an `exn` reference, eg it is a 938 /// `func` reference. 939 /// 940 /// Returns `Some(None)` if this `Ref` is a null `exn` reference. 941 /// 942 /// Returns `Some(Some(_))` if this `Ref` is a non-null `exn` reference. 943 #[inline] 944 pub fn as_exn(&self) -> Option<Option<&Rooted<ExnRef>>> { 945 match self { 946 Ref::Exn(e) => Some(e.as_ref()), 947 _ => None, 948 } 949 } 950 951 /// Get the underlying `exn` reference, panicking if this is a different 952 /// kind of reference. 953 /// 954 /// Returns `None` if this `Ref` is a null `exn` reference. 955 /// 956 /// Returns `Some(_)` if this `Ref` is a non-null `exn` reference. 957 #[inline] 958 pub fn unwrap_exn(&self) -> Option<&Rooted<ExnRef>> { 959 self.as_exn().expect("Ref::unwrap_exn on non-exn reference") 960 } 961 962 /// Is this a `func` reference? 963 #[inline] 964 pub fn is_func(&self) -> bool { 965 matches!(self, Ref::Func(_)) 966 } 967 968 /// Get the underlying `func` reference, if any. 969 /// 970 /// Returns `None` if this `Ref` is not an `func` reference, eg it is an 971 /// `extern` reference. 972 /// 973 /// Returns `Some(None)` if this `Ref` is a null `func` reference. 974 /// 975 /// Returns `Some(Some(_))` if this `Ref` is a non-null `func` reference. 976 #[inline] 977 pub fn as_func(&self) -> Option<Option<&Func>> { 978 match self { 979 Ref::Func(f) => Some(f.as_ref()), 980 _ => None, 981 } 982 } 983 984 /// Get the underlying `func` reference, panicking if this is a different 985 /// kind of reference. 986 /// 987 /// Returns `None` if this `Ref` is a null `func` reference. 988 /// 989 /// Returns `Some(_)` if this `Ref` is a non-null `func` reference. 990 #[inline] 991 pub fn unwrap_func(&self) -> Option<&Func> { 992 self.as_func() 993 .expect("Ref::unwrap_func on non-func reference") 994 } 995 996 /// Get the type of this reference. 997 /// 998 /// # Errors 999 /// 1000 /// Return an error if this reference has been unrooted. 1001 /// 1002 /// # Panics 1003 /// 1004 /// Panics if this reference is associated with a different store. 1005 pub fn ty(&self, store: impl AsContext) -> Result<RefType> { 1006 self.load_ty(&store.as_context().0) 1007 } 1008 1009 pub(crate) fn load_ty(&self, store: &StoreOpaque) -> Result<RefType> { 1010 assert!(self.comes_from_same_store(store)); 1011 Ok(RefType::new( 1012 self.is_null(), 1013 // NB: We choose the most-specific heap type we can here and let 1014 // subtyping do its thing if callers are matching against a 1015 // `HeapType::Func`. 1016 match self { 1017 Ref::Extern(None) => HeapType::NoExtern, 1018 Ref::Extern(Some(_)) => HeapType::Extern, 1019 1020 Ref::Func(None) => HeapType::NoFunc, 1021 Ref::Func(Some(f)) => HeapType::ConcreteFunc(f.load_ty(store)), 1022 1023 Ref::Any(None) => HeapType::None, 1024 Ref::Any(Some(a)) => a._ty(store)?, 1025 1026 Ref::Exn(None) => HeapType::None, 1027 Ref::Exn(Some(e)) => e._ty(store)?.into(), 1028 }, 1029 )) 1030 } 1031 1032 /// Does this reference value match the given type? 1033 /// 1034 /// Returns an error if the underlying `Rooted` has been unrooted. 1035 /// 1036 /// # Panics 1037 /// 1038 /// Panics if this reference is not associated with the given store. 1039 pub fn matches_ty(&self, store: impl AsContext, ty: &RefType) -> Result<bool> { 1040 self._matches_ty(&store.as_context().0, ty) 1041 } 1042 1043 pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<bool> { 1044 assert!(self.comes_from_same_store(store)); 1045 assert!(ty.comes_from_same_engine(store.engine())); 1046 if self.is_null() && !ty.is_nullable() { 1047 return Ok(false); 1048 } 1049 Ok(match (self, ty.heap_type()) { 1050 (Ref::Extern(_), HeapType::Extern) => true, 1051 (Ref::Extern(None), HeapType::NoExtern) => true, 1052 (Ref::Extern(_), _) => false, 1053 1054 (Ref::Func(_), HeapType::Func) => true, 1055 (Ref::Func(None), HeapType::NoFunc | HeapType::ConcreteFunc(_)) => true, 1056 (Ref::Func(Some(f)), HeapType::ConcreteFunc(func_ty)) => f._matches_ty(store, func_ty), 1057 (Ref::Func(_), _) => false, 1058 1059 (Ref::Any(_), HeapType::Any) => true, 1060 (Ref::Any(Some(a)), HeapType::I31) => a._is_i31(store)?, 1061 (Ref::Any(Some(a)), HeapType::Struct) => a._is_struct(store)?, 1062 (Ref::Any(Some(a)), HeapType::ConcreteStruct(_ty)) => match a._as_struct(store)? { 1063 None => false, 1064 Some(s) => s._matches_ty(store, _ty)?, 1065 }, 1066 (Ref::Any(Some(a)), HeapType::Eq) => a._is_eqref(store)?, 1067 (Ref::Any(Some(a)), HeapType::Array) => a._is_array(store)?, 1068 (Ref::Any(Some(a)), HeapType::ConcreteArray(_ty)) => match a._as_array(store)? { 1069 None => false, 1070 Some(a) => a._matches_ty(store, _ty)?, 1071 }, 1072 ( 1073 Ref::Any(None), 1074 HeapType::None 1075 | HeapType::I31 1076 | HeapType::ConcreteStruct(_) 1077 | HeapType::Struct 1078 | HeapType::ConcreteArray(_) 1079 | HeapType::Array 1080 | HeapType::Eq, 1081 ) => true, 1082 (Ref::Any(_), _) => false, 1083 1084 (Ref::Exn(_), HeapType::Exn) => true, 1085 (Ref::Exn(None), HeapType::NoExn | HeapType::ConcreteExn(_)) => true, 1086 (Ref::Exn(Some(e)), HeapType::ConcreteExn(_)) => { 1087 e._matches_ty(store, &ty.heap_type())? 1088 } 1089 (Ref::Exn(_), _) => false, 1090 }) 1091 } 1092 1093 pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &RefType) -> Result<()> { 1094 if !self.comes_from_same_store(store) { 1095 bail!("reference used with wrong store") 1096 } 1097 if !ty.comes_from_same_engine(store.engine()) { 1098 bail!("type used with wrong engine") 1099 } 1100 if self._matches_ty(store, ty)? { 1101 Ok(()) 1102 } else { 1103 let actual_ty = self.load_ty(store)?; 1104 bail!("type mismatch: expected {ty}, found {actual_ty}") 1105 } 1106 } 1107 1108 pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool { 1109 match self { 1110 Ref::Func(Some(f)) => f.comes_from_same_store(store), 1111 Ref::Func(None) => true, 1112 Ref::Extern(Some(x)) => x.comes_from_same_store(store), 1113 Ref::Extern(None) => true, 1114 Ref::Any(Some(a)) => a.comes_from_same_store(store), 1115 Ref::Any(None) => true, 1116 Ref::Exn(Some(e)) => e.comes_from_same_store(store), 1117 Ref::Exn(None) => true, 1118 } 1119 } 1120 } 1121 1122 #[cfg(test)] 1123 mod tests { 1124 use crate::*; 1125 1126 #[test] 1127 fn size_of_val() { 1128 // Try to keep tabs on the size of `Val` and make sure we don't grow its 1129 // size. 1130 let expected = if cfg!(target_arch = "x86_64") 1131 || cfg!(target_arch = "aarch64") 1132 || cfg!(target_arch = "s390x") 1133 || cfg!(target_arch = "riscv64") 1134 || cfg!(target_arch = "arm") 1135 { 1136 24 1137 } else if cfg!(target_arch = "x86") { 1138 20 1139 } else { 1140 panic!("unsupported architecture") 1141 }; 1142 assert_eq!(std::mem::size_of::<Val>(), expected); 1143 } 1144 1145 #[test] 1146 fn size_of_ref() { 1147 // Try to keep tabs on the size of `Ref` and make sure we don't grow its 1148 // size. 1149 let expected = if cfg!(target_arch = "x86_64") 1150 || cfg!(target_arch = "aarch64") 1151 || cfg!(target_arch = "s390x") 1152 || cfg!(target_arch = "riscv64") 1153 || cfg!(target_arch = "arm") 1154 { 1155 24 1156 } else if cfg!(target_arch = "x86") { 1157 20 1158 } else { 1159 panic!("unsupported architecture") 1160 }; 1161 assert_eq!(std::mem::size_of::<Ref>(), expected); 1162 } 1163 1164 #[test] 1165 #[should_panic] 1166 fn val_matches_ty_wrong_engine() { 1167 let e1 = Engine::default(); 1168 let e2 = Engine::default(); 1169 1170 let t1 = FuncType::new(&e1, None, None); 1171 let t2 = FuncType::new(&e2, None, None); 1172 1173 let mut s1 = Store::new(&e1, ()); 1174 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(())); 1175 1176 // Should panic. 1177 let _ = Val::FuncRef(Some(f)).matches_ty( 1178 &s1, 1179 &ValType::Ref(RefType::new(true, HeapType::ConcreteFunc(t2))), 1180 ); 1181 } 1182 1183 #[test] 1184 #[should_panic] 1185 fn ref_matches_ty_wrong_engine() { 1186 let e1 = Engine::default(); 1187 let e2 = Engine::default(); 1188 1189 let t1 = FuncType::new(&e1, None, None); 1190 let t2 = FuncType::new(&e2, None, None); 1191 1192 let mut s1 = Store::new(&e1, ()); 1193 let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(())); 1194 1195 // Should panic. 1196 let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::ConcreteFunc(t2))); 1197 } 1198 } 1199