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