use core::{fmt, marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32, ops::Deref};
use crate::{
__private, GcValue, abi,
schema::{SchemaKind, ValueKind},
};
pub unsafe trait GcHeapObj: Sized + 'static {
const KIND: __private::SchemaKind;
const SCHEMA: *const __private::Schema;
}
pub unsafe trait GcStruct: Sized + 'static {
const SCHEMA: *const __private::Schema;
type View;
fn allocate(self) -> u32;
fn view(root: &Gc<Self>) -> &Self::View;
}
unsafe impl<T: GcStruct> GcHeapObj for T {
const KIND: __private::SchemaKind = SchemaKind::Struct;
const SCHEMA: *const __private::Schema = <T as GcStruct>::SCHEMA;
}
#[must_use]
pub struct Gc<T: GcHeapObj> {
handle: NonZeroU32,
marker: PhantomData<fn() -> T>,
instance_local: PhantomData<*mut ()>,
}
impl<T: GcStruct> Gc<T> {
pub fn new(value: T) -> Self {
let handle = value.allocate();
unsafe { Self::from_raw(handle) }
}
pub fn view(&self) -> &T::View {
T::view(self)
}
}
impl<T: GcHeapObj> Gc<T> {
pub fn as_raw(&self) -> u32 {
self.handle.get()
}
pub fn into_raw(self) -> u32 {
ManuallyDrop::new(self).handle.get()
}
pub unsafe fn from_raw(handle: u32) -> Self {
Self {
handle: NonZeroU32::new(handle).expect("wasmgc: a root cannot be null"),
marker: PhantomData,
instance_local: PhantomData,
}
}
pub fn ref_eq(this: &Self, other: &Self) -> bool {
unsafe { abi::ref_eq(this.handle.get(), other.handle.get()) != 0 }
}
}
impl<T: GcStruct> Deref for Gc<T> {
type Target = T::View;
fn deref(&self) -> &Self::Target {
T::view(self)
}
}
impl<T: GcHeapObj> Clone for Gc<T> {
fn clone(&self) -> Self {
unsafe { Self::from_raw(abi::ref_clone(self.handle.get())) }
}
}
impl<T: GcHeapObj> Drop for Gc<T> {
fn drop(&mut self) {
unsafe { abi::ref_release(self.handle.get()) }
}
}
impl<T: GcStruct> PartialEq for Gc<T> {
fn eq(&self, other: &Self) -> bool {
Self::ref_eq(self, other)
}
}
impl<T: GcStruct> Eq for Gc<T> {}
impl<T: GcStruct> fmt::Debug for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Gc")
.field("type", &core::any::type_name::<T>())
.field("root", &self.handle)
.finish()
}
}
unsafe impl<T: GcHeapObj> GcValue for Gc<T> {
type Abi = u32;
type SetterValue<'a> = &'a Self;
const KIND: ValueKind = reference_kind::<T>(false);
fn to_abi(&self) -> u32 {
self.as_raw()
}
fn setter_abi(value: &&Self) -> u32 {
value.as_raw()
}
fn into_abi(self) -> u32 {
Gc::into_raw(self)
}
unsafe fn from_abi(value: u32) -> Self {
unsafe { Self::from_raw(value) }
}
}
unsafe impl<T: GcHeapObj> GcValue for Option<Gc<T>> {
type Abi = u32;
type SetterValue<'a> = Option<&'a Gc<T>>;
const KIND: ValueKind = reference_kind::<T>(true);
fn to_abi(&self) -> u32 {
self.as_ref().map_or(0, Gc::as_raw)
}
fn setter_abi(value: &Option<&Gc<T>>) -> u32 {
value.map_or(0, Gc::as_raw)
}
fn into_abi(self) -> u32 {
self.map_or(0, Gc::into_raw)
}
unsafe fn from_abi(value: u32) -> Self {
if value == 0 {
None
} else {
Some(unsafe { Gc::from_raw(value) })
}
}
}
const fn reference_kind<T: GcHeapObj>(nullable: bool) -> ValueKind {
match T::KIND {
SchemaKind::Struct => ValueKind::reference(T::SCHEMA, nullable),
SchemaKind::Array => ValueKind::array(T::SCHEMA, nullable),
}
}