gluon_vm 0.18.3

The virtual machine for executing the gluon programming language
Documentation
use std::{
    fmt,
    marker::PhantomData,
    ops::{Deref, DerefMut},
    ptr::NonNull,
};

use std::sync::RwLock;

use base::types::ArcType;

use crate::{
    Result,
    api::{Opaque, OpaqueValue, Pushable, VmType},
    gc::{Gc, Trace},
    thread::{ActiveThread, RootedThread, Thread, ThreadInternal},
    value::Userdata,
};

pub struct Ref<'a, T>
where
    T: Userdata,
{
    reference: &'a T,
    gluon_reference: Option<RefGuard<T, &'static ()>>,
}

impl<'a, 'b, T> VmType for &'b mut Ref<'a, T>
where
    T: Userdata + VmType,
{
    type Type = T::Type;
    fn make_type(vm: &Thread) -> ArcType {
        T::make_type(vm)
    }
}

impl<'vm, 'a, 'b, T> Pushable<'vm> for &'b mut Ref<'a, T>
where
    T: VmType + Userdata,
{
    fn vm_push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
        Scoped::<T, _>::new(self.reference).vm_push(context)?;
        let value = context.last().unwrap();
        self.gluon_reference = Some(RefGuard {
            gluon_reference: Opaque::from_value(context.thread().root_value(value)),
        });
        Ok(())
    }
}

impl<'a, T> Ref<'a, T>
where
    T: Userdata,
{
    pub fn new(reference: &'a T) -> Self {
        Ref {
            reference,
            gluon_reference: None,
        }
    }
}

pub struct RefMut<'a, T>
where
    T: Userdata,
{
    reference: &'a mut T,
    gluon_reference: Option<RefGuard<T, &'static mut ()>>,
}

impl<'a, 'b, T> VmType for &'b mut RefMut<'a, T>
where
    T: Userdata + VmType,
{
    type Type = T::Type;
    fn make_type(vm: &Thread) -> ArcType {
        T::make_type(vm)
    }
}

impl<'vm, 'a, 'b, T> Pushable<'vm> for &'b mut RefMut<'a, T>
where
    T: VmType + Userdata,
{
    fn vm_push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
        Scoped::<T, _>::new_mut(self.reference).vm_push(context)?;
        let value = context.last().unwrap();
        self.gluon_reference = Some(RefGuard {
            gluon_reference: Opaque::from_value(context.thread().root_value(value)),
        });
        Ok(())
    }
}

impl<'a, T> RefMut<'a, T>
where
    T: Userdata,
{
    pub fn new(reference: &'a mut T) -> Self {
        RefMut {
            reference,
            gluon_reference: None,
        }
    }
}

struct RefGuard<T, M>
where
    T: Userdata,
    M: 'static,
{
    gluon_reference: OpaqueValue<RootedThread, Scoped<T, M>>,
}

impl<T, M> Drop for RefGuard<T, M>
where
    T: Userdata,
    M: 'static,
{
    fn drop(&mut self) {
        Scoped::invalidate(&*self.gluon_reference);
    }
}

pub(crate) struct Scoped<T: ?Sized, M> {
    ptr: RwLock<Option<NonNull<T>>>,
    _marker: PhantomData<M>,
}

unsafe impl<T, M> Send for Scoped<T, M> where T: Send + Sync + ?Sized {}
unsafe impl<T, M> Sync for Scoped<T, M> where T: Send + Sync + ?Sized {}

impl<T, M> fmt::Debug for Scoped<T, M> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Scoped")
    }
}

impl<T> Scoped<T, &'static ()> {
    pub fn new(ptr: &T) -> Self {
        Scoped {
            ptr: RwLock::new(NonNull::new(ptr as *const T as *mut T)),
            _marker: PhantomData,
        }
    }
}

impl<T> Scoped<T, &'static mut ()> {
    pub fn new_mut(ptr: &mut T) -> Self {
        Scoped {
            ptr: RwLock::new(NonNull::new(ptr as *mut T)),
            _marker: PhantomData,
        }
    }

    pub fn write(&self) -> Result<WriteGuard<'_, T>> {
        let ptr = self.ptr.write().unwrap();
        if let None = *ptr {
            return Err("Scoped pointer is invalidated".to_string().into());
        }
        Ok(WriteGuard(ptr))
    }
}

impl<T, M> Scoped<T, M> {
    pub fn read(&self) -> Result<ReadGuard<'_, T>> {
        let ptr = self.ptr.read().unwrap();
        if let None = *ptr {
            return Err("Scoped pointer is invalidated".to_string().into());
        }
        Ok(ReadGuard(ptr))
    }

    pub fn invalidate(&self) {
        *self.ptr.write().unwrap() = None;
    }
}

impl<'vm, T: VmType, M> VmType for Scoped<T, M> {
    type Type = T::Type;
    fn make_type(vm: &Thread) -> ArcType {
        T::make_type(vm)
    }
}

unsafe impl<T, M> Trace for Scoped<T, M>
where
    T: Trace,
{
    fn trace(&self, gc: &mut Gc) {
        if let Some(v) = *self.ptr.read().unwrap() {
            unsafe {
                v.as_ref().trace(gc);
            }
        }
    }
}

impl<T, M> Userdata for Scoped<T, M>
where
    T: Userdata,
    M: 'static,
{
}

#[doc(hidden)]
pub struct ReadGuard<'a, T>(std::sync::RwLockReadGuard<'a, Option<NonNull<T>>>);

impl<'a, T> Deref for ReadGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe {
            match *self.0 {
                Some(v) => &*v.as_ptr(),
                None => panic!("Scoped pointer is invalidated"),
            }
        }
    }
}

#[doc(hidden)]
pub struct WriteGuard<'a, T>(std::sync::RwLockWriteGuard<'a, Option<NonNull<T>>>);

impl<'a, T> Deref for WriteGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe {
            match *self.0 {
                Some(v) => &*v.as_ptr(),
                None => panic!("Scoped pointer is invalidated"),
            }
        }
    }
}

impl<'a, T> DerefMut for WriteGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            match *self.0 {
                Some(v) => &mut *v.as_ptr(),
                None => panic!("Scoped pointer is invalidated"),
            }
        }
    }
}