use crate::garbage_collector::{GcPtr, GcRootPtr, UnsafeTypeInfo};
use crate::{
marshal::Marshal,
reflection::{
equals_argument_type, equals_return_type, ArgumentReflection, ReturnTypeReflection,
},
Runtime,
};
use memory::gc::{GcRuntime, HasIndirectionPtr};
use once_cell::sync::OnceCell;
use std::cell::{Ref, RefCell};
use std::{
marker::PhantomPinned,
mem::MaybeUninit,
pin::Pin,
ptr::{self, NonNull},
rc::Rc,
sync::Arc,
};
#[repr(transparent)]
#[derive(Clone)]
pub struct RawStruct(GcPtr);
impl RawStruct {
pub unsafe fn get_ptr(&self) -> *const u8 {
self.0.deref()
}
}
#[derive(Clone)]
pub struct StructRef<'s> {
raw: RawStruct,
runtime: &'s Runtime,
}
impl<'s> StructRef<'s> {
fn new<'r>(raw: RawStruct, runtime: &'r Runtime) -> Self
where
'r: 's,
{
Self { raw, runtime }
}
pub fn into_raw(self) -> RawStruct {
self.raw
}
pub fn root(self, runtime: Rc<RefCell<Runtime>>) -> RootedStruct {
RootedStruct::new(&self.runtime.gc, runtime, self.raw)
}
pub fn type_info(&self) -> &abi::TypeInfo {
unsafe { &*self.runtime.gc.ptr_type(self.raw.0).into_inner().as_ptr() }
}
unsafe fn field_offset_unchecked<T>(
&self,
struct_info: &abi::StructInfo,
field_idx: usize,
) -> NonNull<T> {
let offset = *struct_info.field_offsets().get_unchecked(field_idx);
NonNull::new_unchecked(self.raw.get_ptr().add(offset as usize).cast::<T>() as *mut _)
}
pub fn get<T: ReturnTypeReflection + Marshal<'s>>(&self, field_name: &str) -> Result<T, String>
where
T: 's,
{
let type_info = self.type_info();
let struct_info = type_info.as_struct().unwrap();
let field_idx =
abi::StructInfo::find_field_index(type_info.name(), struct_info, field_name)?;
let field_type = unsafe { struct_info.field_types().get_unchecked(field_idx) };
equals_return_type::<T>(field_type).map_err(|(expected, found)| {
format!(
"Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
type_info.name(),
field_name,
expected,
found,
)
})?;
let field_ptr =
unsafe { self.field_offset_unchecked::<T::MunType>(struct_info, field_idx) };
Ok(Marshal::marshal_from_ptr(
field_ptr,
self.runtime,
Some(field_type),
))
}
pub fn replace<T: ArgumentReflection + Marshal<'s>>(
&mut self,
field_name: &str,
value: T,
) -> Result<T, String>
where
T: 's,
{
let type_info = self.type_info();
let struct_info = type_info.as_struct().unwrap();
let field_idx =
abi::StructInfo::find_field_index(type_info.name(), struct_info, field_name)?;
let field_type = unsafe { struct_info.field_types().get_unchecked(field_idx) };
equals_argument_type(self.runtime, field_type, &value).map_err(|(expected, found)| {
format!(
"Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
type_info.name(),
field_name,
expected,
found,
)
})?;
let field_ptr =
unsafe { self.field_offset_unchecked::<T::MunType>(struct_info, field_idx) };
let old = Marshal::marshal_from_ptr(field_ptr, self.runtime, Some(field_type));
Marshal::marshal_to_ptr(value, field_ptr, Some(field_type));
Ok(old)
}
pub fn set<T: ArgumentReflection + Marshal<'s>>(
&mut self,
field_name: &str,
value: T,
) -> Result<(), String> {
let type_info = self.type_info();
let struct_info = type_info.as_struct().unwrap();
let field_idx =
abi::StructInfo::find_field_index(type_info.name(), struct_info, field_name)?;
let field_type = unsafe { struct_info.field_types().get_unchecked(field_idx) };
equals_argument_type(self.runtime, field_type, &value).map_err(|(expected, found)| {
format!(
"Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
type_info.name(),
field_name,
expected,
found,
)
})?;
let field_ptr =
unsafe { self.field_offset_unchecked::<T::MunType>(struct_info, field_idx) };
Marshal::marshal_to_ptr(value, field_ptr, Some(field_type));
Ok(())
}
}
impl<'r> ArgumentReflection for StructRef<'r> {
fn type_guid(&self, runtime: &Runtime) -> abi::Guid {
unsafe { runtime.gc().ptr_type(self.raw.0).into_inner().as_ref().guid }
}
fn type_name(&self, runtime: &Runtime) -> &str {
unsafe { (&*runtime.gc().ptr_type(self.raw.0).into_inner().as_ptr()).name() }
}
}
impl<'r> ReturnTypeReflection for StructRef<'r> {
fn type_name() -> &'static str {
"struct"
}
fn type_guid() -> abi::Guid {
static GUID: OnceCell<abi::Guid> = OnceCell::new();
*GUID.get_or_init(|| abi::Guid(md5::compute(<Self as ReturnTypeReflection>::type_name()).0))
}
}
impl<'s> Marshal<'s> for StructRef<'s> {
type MunType = RawStruct;
fn marshal_from<'r>(value: Self::MunType, runtime: &'r Runtime) -> Self
where
Self: 's,
'r: 's,
{
StructRef::new(value, runtime)
}
fn marshal_into<'r>(self) -> Self::MunType {
self.into_raw()
}
fn marshal_from_ptr<'r>(
ptr: NonNull<Self::MunType>,
runtime: &'r Runtime,
type_info: Option<&abi::TypeInfo>,
) -> StructRef<'s>
where
Self: 's,
'r: 's,
{
let type_info = type_info.unwrap();
let struct_info = type_info.as_struct().unwrap();
let gc_handle = if struct_info.memory_kind == abi::StructMemoryKind::Value {
let mut gc_handle = {
runtime.gc().alloc(
UnsafeTypeInfo::new(unsafe {
NonNull::new_unchecked(type_info as *const abi::TypeInfo as *mut _)
}),
)
};
let src = ptr.cast::<u8>().as_ptr() as *const _;
let dest = unsafe { gc_handle.deref_mut::<u8>() };
let size = type_info.size_in_bytes();
unsafe { ptr::copy_nonoverlapping(src, dest, size) };
gc_handle
} else {
unsafe { *ptr.cast::<GcPtr>().as_ptr() }
};
StructRef::new(RawStruct(gc_handle), runtime)
}
fn marshal_to_ptr(
value: Self,
mut ptr: NonNull<Self::MunType>,
type_info: Option<&abi::TypeInfo>,
) {
let type_info = type_info.unwrap();
let struct_info = type_info.as_struct().unwrap();
if struct_info.memory_kind == abi::StructMemoryKind::Value {
let dest = ptr.cast::<u8>().as_ptr();
let size = type_info.size_in_bytes();
unsafe { ptr::copy_nonoverlapping(value.into_raw().get_ptr(), dest, size as usize) };
} else {
unsafe { *ptr.as_mut() = value.into_raw() };
}
}
}
pub struct RootedStruct {
handle: GcRootPtr,
runtime: Rc<RefCell<Runtime>>,
}
impl RootedStruct {
fn new<G: GcRuntime<UnsafeTypeInfo>>(
gc: &Arc<G>,
runtime: Rc<RefCell<Runtime>>,
raw: RawStruct,
) -> Self {
let handle = {
let runtime_ref = runtime.borrow();
assert!(unsafe { gc.ptr_type(raw.0).into_inner().as_ref().data.is_struct() });
GcRootPtr::new(&runtime_ref.gc, raw.0)
};
Self { runtime, handle }
}
pub unsafe fn as_ref<'r>(&self, runtime: &'r Runtime) -> StructRef<'r> {
StructRef::new(RawStruct(self.handle.handle()), runtime)
}
pub fn by_ref(&self) -> Pin<Box<RootedStructRef>> {
RootedStructRef::new(RawStruct(self.handle.handle()), self.borrow_runtime())
}
pub fn borrow_runtime(&self) -> Ref<Runtime> {
self.runtime.borrow()
}
}
pub struct RootedStructRef<'s> {
runtime: Ref<'s, Runtime>,
struct_ref: MaybeUninit<StructRef<'s>>,
_pin: PhantomPinned,
}
impl<'s> RootedStructRef<'s> {
fn new(raw: RawStruct, runtime: Ref<'s, Runtime>) -> Pin<Box<Self>> {
let struct_ref = RootedStructRef {
runtime,
struct_ref: MaybeUninit::uninit(),
_pin: PhantomPinned,
};
let mut boxed = Box::pin(struct_ref);
let runtime = NonNull::from(&boxed.runtime);
unsafe {
let struct_ref = StructRef::new(raw, &*runtime.as_ptr());
let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed);
Pin::get_unchecked_mut(mut_ref)
.struct_ref
.as_mut_ptr()
.write(struct_ref);
}
boxed
}
}
impl<'s> std::ops::Deref for RootedStructRef<'s> {
type Target = StructRef<'s>;
fn deref(&self) -> &Self::Target {
unsafe { &*self.struct_ref.as_ptr() }
}
}