#![allow(missing_docs)] use std::any::Any; use std::cell::{self, RefCell}; use std::fmt; use std::rc::{Rc, Weak}; pub trait HostInfo { fn finalize(&mut self) {} } trait InternalRefBase: Any { fn as_any(&self) -> &dyn Any; fn host_info(&self) -> Option>>; fn set_host_info(&self, info: Option>); fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool; } #[derive(Clone)] pub struct InternalRef(Rc); impl InternalRef { pub fn is_ref(&self) -> bool { let r = self.0.as_any(); Any::is::>(r) } pub fn get_ref(&self) -> HostRef { let r = self.0.as_any(); r.downcast_ref::>() .expect("reference is not T type") .clone() } } struct AnyAndHostInfo { any: Box, host_info: Option>, } impl Drop for AnyAndHostInfo { fn drop(&mut self) { if let Some(info) = &mut self.host_info { info.finalize(); } } } #[derive(Clone)] pub struct OtherRef(Rc>); /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)] pub enum AnyRef { /// A reference to no data. Null, /// A reference to data stored internally in `wasmtime`. Ref(InternalRef), /// A reference to data located outside of `wasmtime`. Other(OtherRef), } impl AnyRef { /// Creates a new instance of `AnyRef` from `Box`. pub fn new(data: Box) -> Self { let info = AnyAndHostInfo { any: data, host_info: None, }; AnyRef::Other(OtherRef(Rc::new(RefCell::new(info)))) } /// Creates a `Null` reference. pub fn null() -> Self { AnyRef::Null } /// Returns the data stored in the reference if available. /// # Panics /// Panics if the variant isn't `AnyRef::Other`. pub fn data(&self) -> cell::Ref> { match self { AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any), _ => panic!("expected AnyRef::Other"), } } /// Returns true if the two `AnyRef`'s point to the same value (not just /// values that compare as equal). pub fn ptr_eq(&self, other: &AnyRef) -> bool { match (self, other) { (AnyRef::Null, AnyRef::Null) => true, (AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => { a.ptr_eq(b.as_ref()) } (AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b), _ => false, } } /// Returns a mutable reference to the host information if available. /// # Panics /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`. pub fn host_info(&self) -> Option>> { match self { AnyRef::Null => panic!("null"), AnyRef::Ref(r) => r.0.host_info(), AnyRef::Other(r) => { let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info); if info.is_none() { return None; } Some(cell::RefMut::map(info, |info| info.as_mut().unwrap())) } } } /// Sets the host information for an `AnyRef`. /// # Panics /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`. pub fn set_host_info(&self, info: Option>) { match self { AnyRef::Null => panic!("null"), AnyRef::Ref(r) => r.0.set_host_info(info), AnyRef::Other(r) => { r.0.borrow_mut().host_info = info; } } } } impl fmt::Debug for AnyRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AnyRef::Null => write!(f, "null"), AnyRef::Ref(_) => write!(f, "anyref"), AnyRef::Other(_) => write!(f, "other ref"), } } } struct ContentBox { content: T, host_info: Option>, anyref_data: Weak, } impl Drop for ContentBox { fn drop(&mut self) { if let Some(info) = &mut self.host_info { info.finalize(); } } } /// Represents a piece of data located in the host environment. pub struct HostRef(Rc>>); impl HostRef { /// Creates a new `HostRef` from `T`. pub fn new(item: T) -> HostRef { let anyref_data: Weak> = Weak::new(); let content = ContentBox { content: item, host_info: None, anyref_data, }; HostRef(Rc::new(RefCell::new(content))) } /// Immutably borrows the wrapped data. /// # Panics /// Panics if the value is currently mutably borrowed. pub fn borrow(&self) -> cell::Ref { cell::Ref::map(self.0.borrow(), |b| &b.content) } /// Mutably borrows the wrapped data. /// # Panics /// Panics if the `HostRef` is already borrowed. pub fn borrow_mut(&self) -> cell::RefMut { cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.content) } /// Returns true if the two `HostRef`'s point to the same value (not just /// values that compare as equal). pub fn ptr_eq(&self, other: &HostRef) -> bool { Rc::ptr_eq(&self.0, &other.0) } /// Returns an opaque reference to the wrapped data in the form of /// an `AnyRef`. /// # Panics /// Panics if `HostRef` is already mutably borrowed. pub fn anyref(&self) -> AnyRef { let r = self.0.borrow_mut().anyref_data.upgrade(); if let Some(r) = r { return AnyRef::Ref(InternalRef(r)); } let anyref_data: Rc = Rc::new(self.clone()); self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data); AnyRef::Ref(InternalRef(anyref_data)) } } impl InternalRefBase for HostRef { fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool { if let Some(other) = other.as_any().downcast_ref() { self.ptr_eq(other) } else { false } } fn as_any(&self) -> &dyn Any { self } fn host_info(&self) -> Option>> { let info = cell::RefMut::map(self.0.borrow_mut(), |b| &mut b.host_info); if info.is_none() { return None; } Some(cell::RefMut::map(info, |info| info.as_mut().unwrap())) } fn set_host_info(&self, info: Option>) { self.0.borrow_mut().host_info = info; } } impl Clone for HostRef { fn clone(&self) -> HostRef { HostRef(self.0.clone()) } } impl fmt::Debug for HostRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Ref(")?; self.0.borrow().content.fmt(f)?; write!(f, ")") } }