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