1 //! Implementation of `externref` in Wasmtime. 2 3 use crate::prelude::*; 4 use crate::runtime::vm::VMGcRef; 5 use crate::{ 6 store::{AutoAssertNoGc, StoreOpaque}, 7 AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted, RefType, 8 Result, RootSet, Rooted, StoreContext, StoreContextMut, ValRaw, ValType, WasmTy, 9 }; 10 use core::any::Any; 11 use core::mem; 12 use core::mem::MaybeUninit; 13 14 /// An opaque, GC-managed reference to some host data that can be passed to 15 /// WebAssembly. 16 /// 17 /// The `ExternRef` type represents WebAssembly `externref` values. Wasm can't 18 /// do anything with the `externref`s other than put them in tables, globals, 19 /// and locals or pass them to other functions (such as imported functions from 20 /// the host). Unlike `anyref`s, Wasm guests cannot directly allocate new 21 /// `externref`s; only the host can. 22 /// 23 /// You can use `ExternRef` to give access to host objects and control the 24 /// operations that Wasm can perform on them via what functions you allow Wasm 25 /// to import. 26 /// 27 /// Like all WebAssembly references, these are opaque and unforgeable to Wasm: 28 /// they cannot be faked and Wasm cannot, for example, cast the integer 29 /// `0x12345678` into a reference, pretend it is a valid `externref`, and trick 30 /// the host into dereferencing it and segfaulting or worse. 31 /// 32 /// Note that you can also use `Rooted<ExternRef>` and 33 /// `ManuallyRooted<ExternRef>` as a type parameter with 34 /// [`Func::typed`][crate::Func::typed]- and 35 /// [`Func::wrap`][crate::Func::wrap]-style APIs. 36 /// 37 /// # Example 38 /// 39 /// ``` 40 /// # use wasmtime::*; 41 /// # use std::borrow::Cow; 42 /// # fn _foo() -> Result<()> { 43 /// let engine = Engine::default(); 44 /// let mut store = Store::new(&engine, ()); 45 /// 46 /// // Define some APIs for working with host strings from Wasm via `externref`. 47 /// let mut linker = Linker::new(&engine); 48 /// linker.func_wrap( 49 /// "host-string", 50 /// "new", 51 /// |caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> { 52 /// ExternRef::new(caller, Cow::from("")) 53 /// }, 54 /// )?; 55 /// linker.func_wrap( 56 /// "host-string", 57 /// "concat", 58 /// |mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> { 59 /// let mut s = a 60 /// .data(&caller)? 61 /// .downcast_ref::<Cow<str>>() 62 /// .ok_or_else(|| Error::msg("externref was not a string"))? 63 /// .clone() 64 /// .into_owned(); 65 /// let b = b 66 /// .data(&caller)? 67 /// .downcast_ref::<Cow<str>>() 68 /// .ok_or_else(|| Error::msg("externref was not a string"))?; 69 /// s.push_str(&b); 70 /// ExternRef::new(&mut caller, s) 71 /// }, 72 /// )?; 73 /// 74 /// // Here is a Wasm module that uses those APIs. 75 /// let module = Module::new( 76 /// &engine, 77 /// r#" 78 /// (module 79 /// (import "host-string" "concat" (func $concat (param externref externref) 80 /// (result externref))) 81 /// (func (export "run") (param externref externref) (result externref) 82 /// local.get 0 83 /// local.get 1 84 /// call $concat 85 /// ) 86 /// ) 87 /// "#, 88 /// )?; 89 /// 90 /// // Create a couple `externref`s wrapping `Cow<str>`s. 91 /// let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?; 92 /// let world = ExternRef::new(&mut store, Cow::from("World!"))?; 93 /// 94 /// // Instantiate the module and pass the `externref`s into it. 95 /// let instance = linker.instantiate(&mut store, &module)?; 96 /// let result = instance 97 /// .get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")? 98 /// .call(&mut store, (hello, world))?; 99 /// 100 /// // The module should have concatenated the strings together! 101 /// assert_eq!( 102 /// result.data(&store)?.downcast_ref::<Cow<str>>().unwrap(), 103 /// "Hello, World!" 104 /// ); 105 /// # Ok(()) 106 /// # } 107 /// ``` 108 #[derive(Debug, Clone)] 109 #[repr(transparent)] 110 pub struct ExternRef { 111 pub(crate) inner: GcRootIndex, 112 } 113 114 unsafe impl GcRefImpl for ExternRef { 115 #[allow(private_interfaces)] 116 fn transmute_ref(index: &GcRootIndex) -> &Self { 117 // Safety: `ExternRef` is a newtype of a `GcRootIndex`. 118 let me: &Self = unsafe { mem::transmute(index) }; 119 120 // Assert we really are just a newtype of a `GcRootIndex`. 121 assert!(matches!( 122 me, 123 Self { 124 inner: GcRootIndex { .. }, 125 } 126 )); 127 128 me 129 } 130 } 131 132 impl ExternRef { 133 /// Creates a new instance of `ExternRef` wrapping the given value. 134 /// 135 /// The resulting value is automatically unrooted when the given `context`'s 136 /// scope is exited. See [`Rooted<T>`][crate::Rooted]'s documentation for 137 /// more details. 138 /// 139 /// This method will *not* automatically trigger a GC to free up space in 140 /// the GC heap; instead it will return an error. This gives you more 141 /// precise control over when collections happen and allows you to choose 142 /// between performing synchronous and asynchronous collections. 143 /// 144 /// # Errors 145 /// 146 /// If the allocation cannot be satisfied because the GC heap is currently 147 /// out of memory, but performing a garbage collection might free up space 148 /// such that retrying the allocation afterwards might succeed, then a 149 /// `GcHeapOutOfMemory<T>` error is returned. 150 /// 151 /// The `GcHeapOutOfMemory<T>` error contains the host value that the 152 /// `externref` would have wrapped. You can extract that value from this 153 /// error and reuse it when attempting to allocate an `externref` again 154 /// after GC or otherwise do with it whatever you see fit. 155 /// 156 /// # Example 157 /// 158 /// ``` 159 /// # use wasmtime::*; 160 /// # fn _foo() -> Result<()> { 161 /// let mut store = Store::<()>::default(); 162 /// 163 /// { 164 /// let mut scope = RootScope::new(&mut store); 165 /// 166 /// // Create an `externref` wrapping a `str`. 167 /// let externref = match ExternRef::new(&mut scope, "hello!") { 168 /// Ok(x) => x, 169 /// // If the heap is out of memory, then do a GC and try again. 170 /// Err(e) if e.is::<GcHeapOutOfMemory<&'static str>>() => { 171 /// // Do a GC! Note: in an async context, you'd want to do 172 /// // `scope.as_context_mut().gc_async().await`. 173 /// scope.as_context_mut().gc(); 174 /// 175 /// // Extract the original host value from the error. 176 /// let host_value = e 177 /// .downcast::<GcHeapOutOfMemory<&'static str>>() 178 /// .unwrap() 179 /// .into_inner(); 180 /// 181 /// // Try to allocate the `externref` again, now that the GC 182 /// // has hopefully freed up some space. 183 /// ExternRef::new(&mut scope, host_value)? 184 /// } 185 /// Err(e) => return Err(e), 186 /// }; 187 /// 188 /// // Use the `externref`, pass it to Wasm, etc... 189 /// } 190 /// 191 /// // The `externref` is automatically unrooted when we exit the scope. 192 /// # Ok(()) 193 /// # } 194 /// ``` 195 pub fn new<T>(mut context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>> 196 where 197 T: 'static + Any + Send + Sync, 198 { 199 let ctx = context.as_context_mut().0; 200 201 let value: Box<dyn Any + Send + Sync> = Box::new(value); 202 let gc_ref = ctx 203 .gc_store_mut()? 204 .alloc_externref(value) 205 .err2anyhow() 206 .context("unrecoverable error when allocating new `externref`")? 207 .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap())) 208 .err2anyhow() 209 .context("failed to allocate `externref`")?; 210 211 let mut ctx = AutoAssertNoGc::new(ctx); 212 Ok(Self::from_cloned_gc_ref(&mut ctx, gc_ref.into())) 213 } 214 215 /// Creates a new, manually-rooted instance of `ExternRef` wrapping the 216 /// given value. 217 /// 218 /// The resulting value must be manually unrooted, or else it will leak for 219 /// the entire duration of the store's lifetime. See 220 /// [`ManuallyRooted<T>`][crate::ManuallyRooted]'s documentation for more 221 /// details. 222 /// 223 /// # Errors 224 /// 225 /// This function returns the same errors in the same scenarios as 226 /// [`ExternRef::new`][crate::ExternRef::new]. 227 /// 228 /// # Example 229 /// 230 /// ``` 231 /// # use wasmtime::*; 232 /// # fn _foo() -> Result<()> { 233 /// let mut store = Store::<()>::default(); 234 /// 235 /// // Create a manually-rooted `externref` wrapping a `str`. 236 /// let externref = ExternRef::new_manually_rooted(&mut store, "hello!")?; 237 /// 238 /// // Use `externref` a bunch, pass it to Wasm, etc... 239 /// 240 /// // Don't forget to explicitly unroot the `externref` when you're done 241 /// // using it! 242 /// externref.unroot(&mut store); 243 /// # Ok(()) 244 /// # } 245 /// ``` 246 pub fn new_manually_rooted<T>( 247 mut store: impl AsContextMut, 248 value: T, 249 ) -> Result<ManuallyRooted<ExternRef>> 250 where 251 T: 'static + Any + Send + Sync, 252 { 253 let ctx = store.as_context_mut().0; 254 255 let value: Box<dyn Any + Send + Sync> = Box::new(value); 256 let gc_ref = ctx 257 .gc_store_mut()? 258 .alloc_externref(value) 259 .err2anyhow() 260 .context("unrecoverable error when allocating new `externref`")? 261 .map_err(|x| GcHeapOutOfMemory::<T>::new(*x.downcast().unwrap())) 262 .err2anyhow() 263 .context("failed to allocate `externref`")?; 264 265 let mut ctx = AutoAssertNoGc::new(ctx); 266 Ok(ManuallyRooted::new(&mut ctx, gc_ref.into())) 267 } 268 269 /// Create a new `Rooted<ExternRef>` from the given GC reference. 270 /// 271 /// Does not invoke the `GcRuntime`'s clone hook; callers should ensure it 272 /// has been called. 273 /// 274 /// `gc_ref` should be a GC reference pointing to an instance of `externref` 275 /// that is in this store's GC heap. Failure to uphold this invariant is 276 /// memory safe but will result in general incorrectness such as panics and 277 /// wrong results. 278 pub(crate) fn from_cloned_gc_ref( 279 store: &mut AutoAssertNoGc<'_>, 280 gc_ref: VMGcRef, 281 ) -> Rooted<Self> { 282 assert!( 283 gc_ref.is_extern_ref(&*store.unwrap_gc_store().gc_heap), 284 "GC reference {gc_ref:#p} is not an externref" 285 ); 286 Rooted::new(store, gc_ref) 287 } 288 289 /// Get a shared borrow of the underlying data for this `ExternRef`. 290 /// 291 /// Returns an error if this `externref` GC reference has been unrooted (eg 292 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it 293 /// was rooted within). See the documentation for 294 /// [`Rooted<T>`][crate::Rooted] for more details. 295 /// 296 /// # Example 297 /// 298 /// ``` 299 /// # use wasmtime::*; 300 /// # fn _foo() -> Result<()> { 301 /// let mut store = Store::<()>::default(); 302 /// 303 /// let externref = ExternRef::new(&mut store, "hello")?; 304 /// 305 /// // Access the `externref`'s host data. 306 /// let data = externref.data(&store)?; 307 /// // Dowcast it to a `&str`. 308 /// let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?; 309 /// // We should have got the data we created the `externref` with! 310 /// assert_eq!(*data, "hello"); 311 /// # Ok(()) 312 /// # } 313 /// ``` 314 pub fn data<'a, T>( 315 &self, 316 store: impl Into<StoreContext<'a, T>>, 317 ) -> Result<&'a (dyn Any + Send + Sync)> 318 where 319 T: 'a, 320 { 321 let store = store.into().0; 322 let gc_ref = self.inner.unchecked_try_gc_ref(&store)?; 323 let externref = gc_ref.as_externref_unchecked(); 324 Ok(store.gc_store()?.externref_host_data(externref)) 325 } 326 327 /// Get an exclusive borrow of the underlying data for this `ExternRef`. 328 /// 329 /// Returns an error if this `externref` GC reference has been unrooted (eg 330 /// if you attempt to use a `Rooted<ExternRef>` after exiting the scope it 331 /// was rooted within). See the documentation for 332 /// [`Rooted<T>`][crate::Rooted] for more details. 333 /// 334 /// # Example 335 /// 336 /// ``` 337 /// # use wasmtime::*; 338 /// # fn _foo() -> Result<()> { 339 /// let mut store = Store::<()>::default(); 340 /// 341 /// let externref = ExternRef::new::<usize>(&mut store, 0)?; 342 /// 343 /// // Access the `externref`'s host data. 344 /// let data = externref.data_mut(&mut store)?; 345 /// // Dowcast it to a `usize`. 346 /// let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?; 347 /// // We initialized to zero. 348 /// assert_eq!(*data, 0); 349 /// // And we can mutate the value! 350 /// *data += 10; 351 /// # Ok(()) 352 /// # } 353 /// ``` 354 pub fn data_mut<'a, T>( 355 &self, 356 store: impl Into<StoreContextMut<'a, T>>, 357 ) -> Result<&'a mut (dyn Any + Send + Sync)> 358 where 359 T: 'a, 360 { 361 let store = store.into().0; 362 let gc_ref = self.inner.unchecked_try_gc_ref(store)?.unchecked_copy(); 363 let externref = gc_ref.as_externref_unchecked(); 364 Ok(store.gc_store_mut()?.externref_host_data_mut(externref)) 365 } 366 367 /// Creates a new strongly-owned [`ExternRef`] from the raw value provided. 368 /// 369 /// This is intended to be used in conjunction with [`Func::new_unchecked`], 370 /// [`Func::call_unchecked`], and [`ValRaw`] with its `externref` field. 371 /// 372 /// This function assumes that `raw` is an externref value which is 373 /// currently rooted within the [`Store`]. 374 /// 375 /// # Unsafety 376 /// 377 /// This function is particularly `unsafe` because `raw` not only must be a 378 /// valid externref value produced prior by `to_raw` but it must also be 379 /// correctly rooted within the store. When arguments are provided to a 380 /// callback with [`Func::new_unchecked`], for example, or returned via 381 /// [`Func::call_unchecked`], if a GC is performed within the store then 382 /// floating externref values are not rooted and will be GC'd, meaning that 383 /// this function will no longer be safe to call with the values cleaned up. 384 /// This function must be invoked *before* possible GC operations can happen 385 /// (such as calling wasm). 386 /// 387 /// When in doubt try to not use this. Instead use the safe Rust APIs of 388 /// [`TypedFunc`] and friends. 389 /// 390 /// [`Func::call_unchecked`]: crate::Func::call_unchecked 391 /// [`Func::new_unchecked`]: crate::Func::new_unchecked 392 /// [`Store`]: crate::Store 393 /// [`TypedFunc`]: crate::TypedFunc 394 /// [`ValRaw`]: crate::ValRaw 395 pub unsafe fn from_raw(mut store: impl AsContextMut, raw: u32) -> Option<Rooted<ExternRef>> { 396 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 397 Self::_from_raw(&mut store, raw) 398 } 399 400 // (Not actually memory unsafe since we have indexed GC heaps.) 401 pub(crate) fn _from_raw(store: &mut AutoAssertNoGc, raw: u32) -> Option<Rooted<ExternRef>> { 402 let gc_ref = VMGcRef::from_raw_u32(raw)?; 403 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 404 Some(Self::from_cloned_gc_ref(store, gc_ref)) 405 } 406 407 /// Converts this [`ExternRef`] to a raw value suitable to store within a 408 /// [`ValRaw`]. 409 /// 410 /// Returns an error if this `externref` has been unrooted. 411 /// 412 /// # Unsafety 413 /// 414 /// Produces a raw value which is only safe to pass into a store if a GC 415 /// doesn't happen between when the value is produce and when it's passed 416 /// into the store. 417 /// 418 /// [`ValRaw`]: crate::ValRaw 419 pub unsafe fn to_raw(&self, mut store: impl AsContextMut) -> Result<u32> { 420 let mut store = AutoAssertNoGc::new(store.as_context_mut().0); 421 self._to_raw(&mut store) 422 } 423 424 pub(crate) fn _to_raw(&self, store: &mut AutoAssertNoGc) -> Result<u32> { 425 let gc_ref = self.inner.try_clone_gc_ref(store)?; 426 let raw = gc_ref.as_raw_u32(); 427 store.unwrap_gc_store_mut().expose_gc_ref_to_wasm(gc_ref); 428 Ok(raw) 429 } 430 } 431 432 unsafe impl WasmTy for Rooted<ExternRef> { 433 #[inline] 434 fn valtype() -> ValType { 435 ValType::Ref(RefType::new(false, HeapType::Extern)) 436 } 437 438 #[inline] 439 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 440 self.comes_from_same_store(store) 441 } 442 443 #[inline] 444 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 445 unreachable!() 446 } 447 448 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 449 let gc_ref = self.inner.try_clone_gc_ref(store)?; 450 let r64 = gc_ref.as_r64(); 451 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref); 452 debug_assert_ne!(r64, 0); 453 let externref = u32::try_from(r64).unwrap(); 454 ptr.write(ValRaw::externref(externref)); 455 Ok(()) 456 } 457 458 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 459 let raw = ptr.get_externref(); 460 debug_assert_ne!(raw, 0); 461 let gc_ref = VMGcRef::from_r64(raw.into()) 462 .expect("valid r64") 463 .expect("non-null"); 464 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 465 ExternRef::from_cloned_gc_ref(store, gc_ref) 466 } 467 } 468 469 unsafe impl WasmTy for Option<Rooted<ExternRef>> { 470 #[inline] 471 fn valtype() -> ValType { 472 ValType::EXTERNREF 473 } 474 475 #[inline] 476 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 477 self.map_or(true, |x| x.comes_from_same_store(store)) 478 } 479 480 #[inline] 481 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 482 unreachable!() 483 } 484 485 #[inline] 486 fn is_vmgcref_and_points_to_object(&self) -> bool { 487 self.is_some() 488 } 489 490 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 491 match self { 492 Some(r) => r.store(store, ptr), 493 None => { 494 ptr.write(ValRaw::externref(0)); 495 Ok(()) 496 } 497 } 498 } 499 500 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 501 let gc_ref = VMGcRef::from_r64(ptr.get_externref().into()).expect("valid r64")?; 502 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 503 Some(ExternRef::from_cloned_gc_ref(store, gc_ref)) 504 } 505 } 506 507 unsafe impl WasmTy for ManuallyRooted<ExternRef> { 508 #[inline] 509 fn valtype() -> ValType { 510 ValType::Ref(RefType::new(false, HeapType::Extern)) 511 } 512 513 #[inline] 514 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 515 self.comes_from_same_store(store) 516 } 517 518 #[inline] 519 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 520 unreachable!() 521 } 522 523 #[inline] 524 fn is_vmgcref_and_points_to_object(&self) -> bool { 525 true 526 } 527 528 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 529 let gc_ref = self.inner.try_clone_gc_ref(store)?; 530 let r64 = gc_ref.as_r64(); 531 store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref); 532 debug_assert_ne!(r64, 0); 533 let externref = u32::try_from(r64).unwrap(); 534 ptr.write(ValRaw::externref(externref)); 535 Ok(()) 536 } 537 538 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 539 let raw = ptr.get_externref(); 540 debug_assert_ne!(raw, 0); 541 let gc_ref = VMGcRef::from_r64(raw.into()) 542 .expect("valid r64") 543 .expect("non-null"); 544 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 545 RootSet::with_lifo_scope(store, |store| { 546 let rooted = ExternRef::from_cloned_gc_ref(store, gc_ref); 547 rooted 548 ._to_manually_rooted(store) 549 .expect("rooted is in scope") 550 }) 551 } 552 } 553 554 unsafe impl WasmTy for Option<ManuallyRooted<ExternRef>> { 555 #[inline] 556 fn valtype() -> ValType { 557 ValType::EXTERNREF 558 } 559 560 #[inline] 561 fn compatible_with_store(&self, store: &StoreOpaque) -> bool { 562 self.as_ref() 563 .map_or(true, |x| x.comes_from_same_store(store)) 564 } 565 566 #[inline] 567 fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> { 568 unreachable!() 569 } 570 571 #[inline] 572 fn is_vmgcref_and_points_to_object(&self) -> bool { 573 self.is_some() 574 } 575 576 fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> { 577 match self { 578 Some(r) => r.store(store, ptr), 579 None => { 580 ptr.write(ValRaw::externref(0)); 581 Ok(()) 582 } 583 } 584 } 585 586 unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self { 587 let raw = ptr.get_externref(); 588 debug_assert_ne!(raw, 0); 589 let gc_ref = VMGcRef::from_r64(raw.into()).expect("valid r64")?; 590 let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref); 591 RootSet::with_lifo_scope(store, |store| { 592 let rooted = ExternRef::from_cloned_gc_ref(store, gc_ref); 593 Some( 594 rooted 595 ._to_manually_rooted(store) 596 .expect("rooted is in scope"), 597 ) 598 }) 599 } 600 } 601