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