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