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