1 //! Implementation of `externref` in Wasmtime. 2 3 use super::{AnyRef, RootedGcRefImpl}; 4 use crate::prelude::*; 5 use crate::runtime::vm::{self, VMGcRef}; 6 use crate::store::Asyncness; 7 #[cfg(feature = "async")] 8 use crate::vm::VMStore; 9 use crate::{ 10 AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, OwnedRooted, RefType, 11 Result, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy, 12 store::{AutoAssertNoGc, StoreOpaque, StoreResourceLimiter}, 13 }; 14 use core::any::Any; 15 use core::mem; 16 use core::mem::MaybeUninit; 17 18 /// An opaque, GC-managed reference to some host data that can be passed to 19 /// WebAssembly. 20 /// 21 /// The `ExternRef` type represents WebAssembly `externref` values. Wasm can't 22 /// do anything with the `externref`s other than put them in tables, globals, 23 /// and locals or pass them to other functions (such as imported functions from 24 /// the host). Unlike `anyref`s, Wasm guests cannot directly allocate new 25 /// `externref`s; only the host can. 26 /// 27 /// You can use `ExternRef` to give access to host objects and control the 28 /// operations that Wasm can perform on them via what functions you allow Wasm 29 /// to import. 30 /// 31 /// Like all WebAssembly references, these are opaque and unforgeable to Wasm: 32 /// they cannot be faked and Wasm cannot, for example, cast the integer 33 /// `0x12345678` into a reference, pretend it is a valid `externref`, and trick 34 /// the host into dereferencing it and segfaulting or worse. 35 /// 36 /// Note that you can also use `Rooted<ExternRef>` and 37 /// `OwnedRooted<ExternRef>` as a type parameter with 38 /// [`Func::typed`][crate::Func::typed]- and 39 /// [`Func::wrap`][crate::Func::wrap]-style APIs. 40 /// 41 /// # Example 42 /// 43 /// ``` 44 /// # use wasmtime::*; 45 /// # use std::borrow::Cow; 46 /// # fn _foo() -> Result<()> { 47 /// let engine = Engine::default(); 48 /// let mut store = Store::new(&engine, ()); 49 /// 50 /// // Define some APIs for working with host strings from Wasm via `externref`. 51 /// let mut linker = Linker::new(&engine); 52 /// linker.func_wrap( 53 /// "host-string", 54 /// "new", 55 /// |caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> { 56 /// ExternRef::new(caller, Cow::from("")) 57 /// }, 58 /// )?; 59 /// linker.func_wrap( 60 /// "host-string", 61 /// "concat", 62 /// |mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> { 63 /// let mut s = a 64 /// .data(&caller)? 65 /// .ok_or_else(|| Error::msg("externref has no host data"))? 66 /// .downcast_ref::<Cow<str>>() 67 /// .ok_or_else(|| Error::msg("externref was not a string"))? 68 /// .clone() 69 /// .into_owned(); 70 /// let b = b 71 /// .data(&caller)? 72 /// .ok_or_else(|| Error::msg("externref has no host data"))? 73 /// .downcast_ref::<Cow<str>>() 74 /// .ok_or_else(|| Error::msg("externref was not a string"))?; 75 /// s.push_str(&b); 76 /// ExternRef::new(&mut caller, s) 77 /// }, 78 /// )?; 79 /// 80 /// // Here is a Wasm module that uses those APIs. 81 /// let module = Module::new( 82 /// &engine, 83 /// r#" 84 /// (module 85 /// (import "host-string" "concat" (func $concat (param externref externref) 86 /// (result externref))) 87 /// (func (export "run") (param externref externref) (result externref) 88 /// local.get 0 89 /// local.get 1 90 /// call $concat 91 /// ) 92 /// ) 93 /// "#, 94 /// )?; 95 /// 96 /// // Create a couple `externref`s wrapping `Cow<str>`s. 97 /// let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?; 98 /// let world = ExternRef::new(&mut store, Cow::from("World!"))?; 99 /// 100 /// // Instantiate the module and pass the `externref`s into it. 101 /// let instance = linker.instantiate(&mut store, &module)?; 102 /// let result = instance 103 /// .get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")? 104 /// .call(&mut store, (hello, world))?; 105 /// 106 /// // The module should have concatenated the strings together! 107 /// assert_eq!( 108 /// result 109 /// .data(&store)? 110 /// .expect("externref should have host data") 111 /// .downcast_ref::<Cow<str>>() 112 /// .expect("host data should be a `Cow<str>`"), 113 /// "Hello, World!" 114 /// ); 115 /// # Ok(()) 116 /// # } 117 /// ``` 118 #[derive(Debug, Clone)] 119 #[repr(transparent)] 120 pub struct ExternRef { 121 pub(crate) inner: GcRootIndex, 122 } 123 124 unsafe impl GcRefImpl for ExternRef { transmute_ref(index: &GcRootIndex) -> &Self125 fn transmute_ref(index: &GcRootIndex) -> &Self { 126 // Safety: `ExternRef` is a newtype of a `GcRootIndex`. 127 let me: &Self = unsafe { mem::transmute(index) }; 128 129 // Assert we really are just a newtype of a `GcRootIndex`. 130 assert!(matches!( 131 me, 132 Self { 133 inner: GcRootIndex { .. }, 134 } 135 )); 136 137 me 138 } 139 } 140 141 impl ExternRef { 142 /// Synchronously allocates a new `ExternRef` wrapping the given value. 143 /// 144 /// The resulting value is automatically unrooted when the given `context`'s 145 /// scope is exited. If you need to hold the reference past the `context`'s 146 /// scope, convert the result into an 147 /// [`OwnedRooted<T>`][crate::OwnedRooted]. See the documentation for 148 /// [`Rooted<T>`][crate::Rooted] and 149 /// [`OwnedRooted<T>`][crate::OwnedRooted] for more details. 150 /// 151 /// # Automatic Garbage Collection 152 /// 153 /// If the GC heap is at capacity, and there isn't room for allocating a new 154 /// `externref`, this method will automatically trigger a synchronous 155 /// collection in an attempt to free up space in the GC heap. Note that 156 /// [`ExternRef::new_async`] will perform an async GC if a synchronous GC is 157 /// not desired. 158 /// 159 /// # Errors 160 /// 161 /// If the allocation cannot be satisfied because the GC heap is currently 162 /// out of memory, then a [`GcHeapOutOfMemory<T>`][crate::GcHeapOutOfMemory] 163 /// error is returned. The allocation might succeed on a second attempt if 164 /// you drop some rooted GC references and try again. 165 /// 166 /// The [`GcHeapOutOfMemory<T>`][crate::GcHeapOutOfMemory] error contains 167 /// the host value that the `externref` would have wrapped. You can extract 168 /// that value from the error and reuse it when attempting to allocate an 169 /// `externref` again after dropping rooted GC references and then 170 /// performing a collection or otherwise do with it whatever you see fit. 171 /// 172 /// If `store` is configured with a 173 /// [`ResourceLimiterAsync`](crate::ResourceLimiterAsync) then an error will 174 /// be returned because [`ExternRef::new_async`] should be used instead. 175 /// 176 /// # Example 177 /// 178 /// ``` 179 /// # use wasmtime::*; 180 /// # fn _foo() -> Result<()> { 181 /// let mut store = Store::<()>::default(); 182 /// 183 /// { 184 /// let mut scope = RootScope::new(&mut store); 185 /// 186 /// // Allocate an `externref` wrapping a `String`. 187 /// let externref = match ExternRef::new(&mut scope, "hello!".to_string()) { 188 /// // The allocation succeeded. 189 /// Ok(x) => x, 190 /// // The allocation failed. 191 /// Err(e) => match e.downcast::<GcHeapOutOfMemory<String>>() { 192 /// // The allocation failed because the GC heap does not have 193 /// // capacity for this allocation. 194 /// Ok(oom) => { 195 /// // Take back ownership of our `String`. 196 /// let s = oom.into_inner(); 197 /// // Drop some rooted GC refs from our system to potentially 198 /// // free up space for Wasmtime to make this allocation. 199 /// # let drop_some_gc_refs = || {}; 200 /// drop_some_gc_refs(); 201 /// // Finally, try to allocate again, reusing the original 202 /// // string. 203 /// ExternRef::new(&mut scope, s)? 204 /// } 205 /// Err(e) => return Err(e), 206 /// }, 207 /// }; 208 /// 209 /// // Use the `externref`, pass it to Wasm, etc... 210 /// } 211 /// 212 /// // The `externref` is automatically unrooted when we exit the scope. 213 /// # Ok(()) 214 /// # } 215 /// ``` new<T>(mut store: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>> where T: 'static + Any + Send + Sync,216 pub fn new<T>(mut store: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>> 217 where 218 T: 'static + Any + Send + Sync, 219 { 220 let (mut limiter, store) = store 221 .as_context_mut() 222 .0 223 .validate_sync_resource_limiter_and_store_opaque()?; 224 vm::assert_ready(Self::_new_async( 225 store, 226 limiter.as_mut(), 227 value, 228 Asyncness::No, 229 )) 230 } 231 232 /// Asynchronously allocates a new `ExternRef` wrapping the given value. 233 /// 234 /// The resulting value is automatically unrooted when the given `context`'s 235 /// scope is exited. If you need to hold the reference past the `context`'s 236 /// scope, convert the result into an 237 /// [`OwnedRooted<T>`][crate::OwnedRooted]. See the documentation for 238 /// [`Rooted<T>`][crate::Rooted] and 239 /// [`OwnedRooted<T>`][crate::OwnedRooted] for more details. 240 /// 241 /// # Automatic Garbage Collection 242 /// 243 /// If the GC heap is at capacity, and there isn't room for allocating a new 244 /// `externref`, this method will automatically trigger an asynchronous 245 /// collection in an attempt to free up space in the GC heap. 246 /// 247 /// # Errors 248 /// 249 /// If the allocation cannot be satisfied because the GC heap is currently 250 /// out of memory, then a [`GcHeapOutOfMemory<T>`][crate::GcHeapOutOfMemory] 251 /// error is returned. The allocation might succeed on a second attempt if 252 /// you drop some rooted GC references and try again. 253 /// 254 /// The [`GcHeapOutOfMemory<T>`][crate::GcHeapOutOfMemory] error contains 255 /// the host value that the `externref` would have wrapped. You can extract 256 /// that value from the error and reuse it when attempting to allocate an 257 /// `externref` again after dropping rooted GC references and then 258 /// performing a collection or otherwise do with it whatever you see fit. 259 /// 260 /// # Example 261 /// 262 /// ``` 263 /// use wasmtime::*; 264 /// 265 /// # async fn _foo() -> Result<()> { 266 /// let mut store = Store::<()>::default(); 267 /// 268 /// { 269 /// let mut scope = RootScope::new(&mut store); 270 /// 271 /// // Create an `externref` wrapping a `String`. 272 /// let externref = match ExternRef::new_async(&mut scope, "hello!".to_string()).await { 273 /// // The allocation succeeded. 274 /// Ok(x) => x, 275 /// // The allocation failed. 276 /// Err(e) => match e.downcast::<GcHeapOutOfMemory<String>>() { 277 /// // The allocation failed because the GC heap does not have 278 /// // capacity for this allocation. 279 /// Ok(oom) => { 280 /// // Take back ownership of our `String`. 281 /// let s = oom.into_inner(); 282 /// // Drop some rooted GC refs from our system to potentially 283 /// // free up space for Wasmtime to make this allocation. 284 /// # let drop_some_gc_refs = || {}; 285 /// drop_some_gc_refs(); 286 /// // Finally, try to allocate again, reusing the original 287 /// // string. 288 /// ExternRef::new_async(&mut scope, s).await? 289 /// } 290 /// Err(e) => return Err(e), 291 /// }, 292 /// }; 293 /// 294 /// // Use the `externref`, pass it to Wasm, etc... 295 /// } 296 /// 297 /// // The `externref` is automatically unrooted when we exit the scope. 298 /// # Ok(()) 299 /// # } 300 /// ``` 301 /// 302 /// # Panics 303 /// 304 /// Panics if the `context` is not configured for async; use 305 /// [`ExternRef::new`][crate::ExternRef::new] to perform synchronous 306 /// allocation instead. 307 #[cfg(feature = "async")] new_async<T>(mut store: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>> where T: 'static + Any + Send + Sync,308 pub async fn new_async<T>(mut store: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>> 309 where 310 T: 'static + Any + Send + Sync, 311 { 312 let (mut limiter, store) = store.as_context_mut().0.resource_limiter_and_store_opaque(); 313 Self::_new_async(store, limiter.as_mut(), value, Asyncness::Yes).await 314 } 315 _new_async<T>( store: &mut StoreOpaque, limiter: Option<&mut StoreResourceLimiter<'_>>, value: T, asyncness: Asyncness, ) -> Result<Rooted<ExternRef>> where T: 'static + Any + Send + Sync,316 pub(crate) async fn _new_async<T>( 317 store: &mut StoreOpaque, 318 limiter: Option<&mut StoreResourceLimiter<'_>>, 319 value: T, 320 asyncness: Asyncness, 321 ) -> Result<Rooted<ExternRef>> 322 where 323 T: 'static + Any + Send + Sync, 324 { 325 // Allocate the box once, regardless how many gc-and-retry attempts we 326 // make. 327 let value: Box<dyn Any + Send + Sync> = Box::new(value); 328 329 let gc_ref = store 330 .retry_after_gc_async(limiter, value, asyncness, |store, value| { 331 store 332 .require_gc_store_mut()? 333 .alloc_externref(value) 334 .context("unrecoverable error when allocating new `externref`")? 335 .map_err(|(x, n)| GcHeapOutOfMemory::new(x, n).into()) 336 }) 337 .await 338 // Translate the `GcHeapOutOfMemory`'s inner value from the boxed 339 // trait object into `T`. 340 .map_err( 341 |e| match e.downcast::<GcHeapOutOfMemory<Box<dyn Any + Send + Sync>>>() { 342 Ok(oom) => oom.map_inner(|x| *x.downcast::<T>().unwrap()).into(), 343 Err(e) => e, 344 }, 345 )?; 346 347 let mut ctx = AutoAssertNoGc::new(store); 348 Ok(Self::from_cloned_gc_ref(&mut ctx, gc_ref.into())) 349 } 350 351 /// Convert an `anyref` into an `externref`. 352 /// 353 /// This is equivalent to the `extern.convert_any` instruction in Wasm. 354 /// 355 /// You can get the underlying `anyref` again via the 356 /// [`AnyRef::convert_extern`] method or the `any.convert_extern` Wasm 357 /// instruction. 358 /// 359 /// The resulting `ExternRef` will not have any host data associated with 360 /// it, so [`ExternRef::data`] and [`ExternRef::data_mut`] will both return 361 /// `None`. 362 /// 363 /// Returns an error if the `anyref` GC reference has been unrooted (eg if 364 /// you attempt to use a `Rooted<AnyRef>` after exiting the scope it was 365 /// rooted within). See the documentation for [`Rooted<T>`][crate::Rooted] 366 /// for more details. 367 /// 368 /// # Example 369 /// 370 /// ``` 371 /// use wasmtime::*; 372 /// # fn foo() -> Result<()> { 373 /// let engine = Engine::default(); 374 /// let mut store = Store::new(&engine, ()); 375 /// 376 /// // Create an `anyref`. 377 /// let i31 = I31::wrapping_u32(0x1234); 378 /// let anyref = AnyRef::from_i31(&mut store, i31); 379 /// 380 /// // Convert that `anyref` into an `externref`. 381 /// let externref = ExternRef::convert_any(&mut store, anyref)?; 382 /// 383 /// // This `externref` doesn't have any associated host data. 384 /// assert!(externref.data(&store)?.is_none()); 385 /// 386 /// // We can convert it back to an `anyref` and get its underlying `i31` 387 /// // data. 388 /// let anyref = AnyRef::convert_extern(&mut store, externref)?; 389 /// assert_eq!(anyref.unwrap_i31(&store)?.get_u32(), 0x1234); 390 /// # Ok(()) } 391 /// # foo().unwrap(); convert_any( mut context: impl AsContextMut, anyref: Rooted<AnyRef>, ) -> Result<Rooted<ExternRef>>392 pub fn convert_any( 393 mut context: impl AsContextMut, 394 anyref: Rooted<AnyRef>, 395 ) -> Result<Rooted<ExternRef>> { 396 let mut store = AutoAssertNoGc::new(context.as_context_mut().0); 397 Self::_convert_any(&mut store, anyref) 398 } 399 _convert_any( store: &mut AutoAssertNoGc<'_>, anyref: Rooted<AnyRef>, ) -> Result<Rooted<ExternRef>>400 pub(crate) fn _convert_any( 401 store: &mut AutoAssertNoGc<'_>, 402 anyref: Rooted<AnyRef>, 403 ) -> Result<Rooted<ExternRef>> { 404 let gc_ref = anyref.try_clone_gc_ref(store)?; 405 Ok(Self::from_cloned_gc_ref(store, gc_ref)) 406 } 407 408 /// Create a new `Rooted<ExternRef>` from the given GC reference. 409 /// 410 /// Does not invoke the `GcRuntime`'s clone hook; callers should ensure it 411 /// has been called. 412 /// 413 /// `gc_ref` should be a GC reference pointing to an instance of `externref` 414 /// that is in this store's GC heap. Failure to uphold this invariant is 415 /// memory safe but will result in general incorrectness such as panics and 416 /// wrong results. from_cloned_gc_ref( store: &mut AutoAssertNoGc<'_>, gc_ref: VMGcRef, ) -> Rooted<Self>417 pub(crate) fn from_cloned_gc_ref( 418 store: &mut AutoAssertNoGc<'_>, 419 gc_ref: VMGcRef, 420 ) -> Rooted<Self> { 421 if !gc_ref.is_i31() { 422 assert!( 423 gc_ref.is_extern_ref(&*store.unwrap_gc_store().gc_heap) 424 || gc_ref.is_any_ref(&*store.unwrap_gc_store().gc_heap), 425 "GC reference {gc_ref:#p} should be an externref or anyref" 426 ); 427 } 428 Rooted::new(store, gc_ref) 429 } 430 431 /// Get a shared borrow of the underlying data for this `ExternRef`. 432 /// 433 /// Returns `None` if this is an `externref` wrapper of an `anyref` created 434 /// by the `extern.convert_any` instruction or the 435 /// [`ExternRef::convert_any`] method. 436 /// 437 /// Returns an error if this `externref` GC reference has been unrooted (eg 438 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it 439 /// was rooted within). See the documentation for 440 /// [`Rooted<T>`][crate::Rooted] for more details. 441 /// 442 /// # Example 443 /// 444 /// ``` 445 /// # use wasmtime::*; 446 /// # fn _foo() -> Result<()> { 447 /// let mut store = Store::<()>::default(); 448 /// 449 /// let externref = ExternRef::new(&mut store, "hello")?; 450 /// 451 /// // Access the `externref`'s host data. 452 /// let data = externref.data(&store)?.ok_or_else(|| Error::msg("no host data"))?; 453 /// // Dowcast it to a `&str`. 454 /// let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?; 455 /// // We should have got the data we created the `externref` with! 456 /// assert_eq!(*data, "hello"); 457 /// # Ok(()) 458 /// # } 459 /// ``` data<'a, T>( &self, store: impl Into<StoreContext<'a, T>>, ) -> Result<Option<&'a (dyn Any + Send + Sync)>> where T: 'static,460 pub fn data<'a, T>( 461 &self, 462 store: impl Into<StoreContext<'a, T>>, 463 ) -> Result<Option<&'a (dyn Any + Send + Sync)>> 464 where 465 T: 'static, 466 { 467 let store = store.into().0; 468 let gc_ref = self.inner.try_gc_ref(&store)?; 469 if gc_ref.is_i31() { 470 return Ok(None); 471 } 472 let gc_store = store.require_gc_store()?; 473 if let Some(externref) = gc_ref.as_externref(&*gc_store.gc_heap) { 474 Ok(Some(gc_store.externref_host_data(externref))) 475 } else { 476 Ok(None) 477 } 478 } 479 480 /// Get an exclusive borrow of the underlying data for this `ExternRef`. 481 /// 482 /// Returns `None` if this is an `externref` wrapper of an `anyref` created 483 /// by the `extern.convert_any` instruction or the 484 /// [`ExternRef::convert_any`] constructor. 485 /// 486 /// Returns an error if this `externref` GC reference has been unrooted (eg 487 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it 488 /// was rooted within). See the documentation for 489 /// [`Rooted<T>`][crate::Rooted] for more details. 490 /// 491 /// # Example 492 /// 493 /// ``` 494 /// # use wasmtime::*; 495 /// # fn _foo() -> Result<()> { 496 /// let mut store = Store::<()>::default(); 497 /// 498 /// let externref = ExternRef::new::<usize>(&mut store, 0)?; 499 /// 500 /// // Access the `externref`'s host data. 501 /// let data = externref.data_mut(&mut store)?.ok_or_else(|| Error::msg("no host data"))?; 502 /// // Dowcast it to a `usize`. 503 /// let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?; 504 /// // We initialized to zero. 505 /// assert_eq!(*data, 0); 506 /// // And we can mutate the value! 507 /// *data += 10; 508 /// # Ok(()) 509 /// # } 510 /// ``` data_mut<'a, T>( &self, store: impl Into<StoreContextMut<'a, T>>, ) -> Result<Option<&'a mut (dyn Any + Send + Sync)>> where T: 'static,511 pub fn data_mut<'a, T>( 512 &self, 513 store: impl Into<StoreContextMut<'a, T>>, 514 ) -> Result<Option<&'a mut (dyn Any + Send + Sync)>> 515 where 516 T: 'static, 517 { 518 let store = store.into().0; 519 // NB: need to do an unchecked copy to release the borrow on the store 520 // so that we can get the store's GC store. But importantly we cannot 521 // trigger a GC while we are working with `gc_ref` here. 522 let gc_ref = self.inner.try_gc_ref(store)?.unchecked_copy(); 523 if gc_ref.is_i31() { 524 return Ok(None); 525 } 526 let gc_store = store.require_gc_store_mut()?; 527 if let Some(externref) = gc_ref.as_externref(&*gc_store.gc_heap) { 528 Ok(Some(gc_store.externref_host_data_mut(externref))) 529 } else { 530 Ok(None) 531 } 532 } 533 534 /// Creates a new strongly-owned [`ExternRef`] from the raw value provided. 535 /// 536 /// This is intended to be used in conjunction with [`Func::new_unchecked`], 537 /// [`Func::call_unchecked`], and [`ValRaw`] with its `externref` field. 538 /// 539 /// This function assumes that `raw` is an externref value which is 540 /// currently rooted within the [`Store`]. 541 /// 542 /// # Correctness 543 /// 544 /// This function is tricky to get right because `raw` not only must be a 545 /// valid `externref` value produced prior by [`ExternRef::to_raw`] but it 546 /// must also be correctly rooted within the store. When arguments are 547 /// provided to a callback with [`Func::new_unchecked`], for example, or 548 /// returned via [`Func::call_unchecked`], if a GC is performed within the 549 /// store then floating `externref` values are not rooted and will be GC'd, 550 /// meaning that this function will no longer be correct to call with the 551 /// values cleaned up. This function must be invoked *before* possible GC 552 /// operations can happen (such as calling Wasm). 553 /// 554 /// 555 /// When in doubt try to not use this. Instead use the Rust APIs of 556 /// [`TypedFunc`] and friends. Note though that this function is not 557 /// `unsafe` as any value can be passed in. Incorrect values can result in 558 /// runtime panics, however, so care must still be taken with this method. 559 /// 560 /// [`Func::call_unchecked`]: crate::Func::call_unchecked 561 /// [`Func::new_unchecked`]: crate::Func::new_unchecked 562 /// [`Store`]: crate::Store 563 /// [`TypedFunc`]: crate::TypedFunc 564 /// [`ValRaw`]: crate::ValRaw from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>>565 pub fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> { 566 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 567 Self::_from_raw(&mut store, raw) 568 } 569 570 // (Not actually memory unsafe since we have indexed GC heaps.) _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<ExternRef>>571 pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<ExternRef>> { 572 let gc_ref = VMGcRef::from_raw_u32(raw)?; 573 let gc_ref = store.clone_gc_ref(&gc_ref); 574 Some(Self::from_cloned_gc_ref(store, gc_ref)) 575 } 576 577 /// Converts this [`ExternRef`] to a raw value suitable to store within a 578 /// [`ValRaw`]. 579 /// 580 /// Returns an error if this `externref` has been unrooted. 581 /// 582 /// # Correctness 583 /// 584 /// Produces a raw value which is only valid to pass into a store if a GC 585 /// doesn't happen between when the value is produce and when it's passed 586 /// into the store. 587 /// 588 /// [`ValRaw`]: crate::ValRaw to_raw(&self, mut store: impl AsContextMut) -> Result<u32>589 pub fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> { 590 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 591 self._to_raw(&mut store) 592 } 593 _to_raw(&self, store: &mut AutoAssertNoGc) -> Result<u32>594 pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc) -> Result<u32> { 595 let gc_ref = self.inner.try_clone_gc_ref(store)?; 596 let raw = store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref); 597 Ok(raw.get()) 598 } 599 } 600 601 unsafe impl WasmTy for Rooted<ExternRef> { 602 #[inline] valtype() -> ValType603 fn valtype() -> ValType { 604 ValType::Ref(RefType::new(false, HeapType::Extern)) 605 } 606 607 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool608 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 609 self.comes_from_same_store(store) 610 } 611 612 #[inline] dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>613 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 614 unreachable!() 615 } 616 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>617 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 618 self.wasm_ty_store(store, ptr, ValRaw::externref) 619 } 620 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self621 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 622 Self::wasm_ty_load(store, ptr.get_externref(), ExternRef::from_cloned_gc_ref) 623 } 624 } 625 626 unsafe impl WasmTy for Option<Rooted<ExternRef>> { 627 #[inline] valtype() -> ValType628 fn valtype() -> ValType { 629 ValType::EXTERNREF 630 } 631 632 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool633 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 634 self.map_or(true, |x| x.comes_from_same_store(store)) 635 } 636 637 #[inline] dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>638 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 639 unreachable!() 640 } 641 642 #[inline] is_vmgcref_and_points_to_object(&self) -> bool643 fn is_vmgcref_and_points_to_object(&self) -> bool { 644 self.is_some() 645 } 646 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>647 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 648 <Rooted<ExternRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::externref) 649 } 650 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self651 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 652 <Rooted<ExternRef>>::wasm_ty_option_load( 653 store, 654 ptr.get_externref(), 655 ExternRef::from_cloned_gc_ref, 656 ) 657 } 658 } 659 660 unsafe impl WasmTy for OwnedRooted<ExternRef> { 661 #[inline] valtype() -> ValType662 fn valtype() -> ValType { 663 ValType::Ref(RefType::new(false, HeapType::Extern)) 664 } 665 666 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool667 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 668 self.comes_from_same_store(store) 669 } 670 671 #[inline] dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>672 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 673 unreachable!() 674 } 675 676 #[inline] is_vmgcref_and_points_to_object(&self) -> bool677 fn is_vmgcref_and_points_to_object(&self) -> bool { 678 true 679 } 680 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>681 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 682 self.wasm_ty_store(store, ptr, ValRaw::externref) 683 } 684 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self685 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 686 Self::wasm_ty_load(store, ptr.get_externref(), ExternRef::from_cloned_gc_ref) 687 } 688 } 689 690 unsafe impl WasmTy for Option<OwnedRooted<ExternRef>> { 691 #[inline] valtype() -> ValType692 fn valtype() -> ValType { 693 ValType::EXTERNREF 694 } 695 696 #[inline] compatible_with_store(&self, store: &StoreOpaque) -> bool697 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 698 self.as_ref() 699 .map_or(true, |x| x.comes_from_same_store(store)) 700 } 701 702 #[inline] dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>703 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 704 unreachable!() 705 } 706 707 #[inline] is_vmgcref_and_points_to_object(&self) -> bool708 fn is_vmgcref_and_points_to_object(&self) -> bool { 709 self.is_some() 710 } 711 store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>712 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 713 <OwnedRooted<ExternRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::externref) 714 } 715 load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self716 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 717 <OwnedRooted<ExternRef>>::wasm_ty_option_load( 718 store, 719 ptr.get_externref(), 720 ExternRef::from_cloned_gc_ref, 721 ) 722 } 723 } 724