#[cfg(wasm)]
pub type DartAbi = wasm_bindgen::JsValue;
#[cfg(not(wasm))]
pub type DartAbi = allo_isolate::ffi::DartCObject;
#[cfg(not(wasm))]
use dart_sys::Dart_PersistentHandle;
use log::warn;
use std::{mem, ops, sync::Arc, thread::ThreadId};
#[cfg(not(wasm))]
pub use allo_isolate::IntoDart;
#[cfg(wasm)]
pub type MessagePort = web::PortLike;
#[cfg(not(wasm))]
pub type MessagePort = i64;
#[cfg(wasm)]
pub type OpaqueMessagePort = wasm_bindgen::JsValue;
#[cfg(not(wasm))]
pub type OpaqueMessagePort = i64;
#[cfg(wasm)]
pub type DartWrapObject = wasm_bindgen::JsValue;
#[cfg(not(wasm))]
pub type DartWrapObject = DartHandleWrap;
#[cfg(wasm)]
pub type DartObject = wasm_bindgen::JsValue;
#[cfg(not(wasm))]
pub type DartObject = Dart_PersistentHandle;
#[cfg(wasm)]
pub mod web;
#[cfg(wasm)]
pub use web::*;
#[cfg(not(wasm))]
pub type Channel = allo_isolate::Isolate;
#[cfg(not(wasm))]
pub mod io;
#[cfg(not(wasm))]
pub use io::*;
use crate::DartSafe;
#[cfg(feature = "uuid")]
const UUID_SIZE_IN_BYTES: usize = 16;
#[cfg(feature = "uuid")]
#[inline]
pub fn wire2api_uuid_ref(id: &[u8]) -> uuid::Uuid {
uuid::Uuid::from_bytes(
*<&[u8] as std::convert::TryInto<&[u8; UUID_SIZE_IN_BYTES]>>::try_into(id)
.expect("invalid uuid slice"),
)
}
#[cfg(feature = "uuid")]
#[inline]
pub fn wire2api_uuid(id: Vec<u8>) -> uuid::Uuid {
wire2api_uuid_ref(id.as_slice())
}
#[cfg(feature = "uuid")]
#[inline]
pub fn wire2api_uuids(ids: Vec<u8>) -> Vec<uuid::Uuid> {
ids.as_slice()
.chunks(UUID_SIZE_IN_BYTES)
.map(wire2api_uuid_ref)
.collect::<Vec<uuid::Uuid>>()
}
#[repr(transparent)]
#[derive(Debug)]
pub struct RustOpaque<T: ?Sized + DartSafe> {
ptr: Option<Arc<T>>,
}
impl<T: DartSafe> RustOpaque<T> {
pub fn try_unwrap(self) -> Result<T, Self> {
if let Some(ptr) = self.ptr {
Arc::try_unwrap(ptr).map_err(RustOpaque::from)
} else {
panic!("Use after free.")
}
}
}
impl<T: ?Sized + DartSafe> Clone for RustOpaque<T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr.clone(),
}
}
}
pub unsafe fn opaque_from_dart<T: DartSafe>(ptr: *const T) -> RustOpaque<T> {
RustOpaque {
ptr: (!ptr.is_null()).then(|| Arc::from_raw(ptr)),
}
}
impl<T: ?Sized + DartSafe> ops::Deref for RustOpaque<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
if let Some(ptr) = &self.ptr {
ptr.as_ref()
} else {
panic!("Use after free.")
}
}
}
impl<T: ?Sized + DartSafe> From<Arc<T>> for RustOpaque<T> {
fn from(ptr: Arc<T>) -> Self {
Self { ptr: Some(ptr) }
}
}
impl<T: DartSafe> RustOpaque<T> {
pub fn new(value: T) -> Self {
Self {
ptr: Some(Arc::new(value)),
}
}
}
impl<T: DartSafe> From<RustOpaque<T>> for DartAbi {
fn from(value: RustOpaque<T>) -> Self {
let ptr = if let Some(ptr) = value.ptr {
Arc::into_raw(ptr)
} else {
std::ptr::null()
};
let size = mem::size_of::<T>();
vec![ptr.into_dart(), size.into_dart()].into_dart()
}
}
#[derive(Debug)]
pub struct DartOpaque {
handle: Option<DartOpaqueBase>,
thread_id: ThreadId,
}
unsafe impl Send for DartOpaque {}
unsafe impl Sync for DartOpaque {}
impl DartOpaque {
pub unsafe fn new(handle: DartObject, port: OpaqueMessagePort) -> Self {
Self {
handle: Some(DartOpaqueBase::new(handle, Some(port))),
thread_id: std::thread::current().id(),
}
}
pub unsafe fn new_non_droppable(handle: DartObject) -> Self {
Self {
handle: Some(DartOpaqueBase::new(handle, None)),
thread_id: std::thread::current().id(),
}
}
pub fn try_unwrap(mut self) -> Result<DartWrapObject, Self> {
if std::thread::current().id() == self.thread_id {
Ok(self.handle.take().unwrap().unwrap())
} else {
Err(self)
}
}
}
impl From<DartOpaque> for DartAbi {
fn from(mut data: DartOpaque) -> Self {
data.handle.take().unwrap().into_raw().into_dart()
}
}
impl Drop for DartOpaque {
fn drop(&mut self) {
if let Some(inner) = self.handle.take() {
if std::thread::current().id() != self.thread_id {
if let Some(channel) = inner.channel() {
let ptr = inner.into_raw();
if !channel.post(ptr) {
warn!("Drop DartOpaque after closing the port.");
};
} else {
warn!("Drop non droppable DartOpaque.");
}
}
}
}
}
#[macro_export]
macro_rules! opaque_dyn {
($ex:expr) => {
$crate::RustOpaque::new(::std::boxed::Box::new($ex))
};
}