luau-vm 0.732.0

Pure-Rust Luau virtual machine, garbage collector, and standard libraries
Documentation
use core::mem::{ManuallyDrop, size_of};
use core::ptr::{self, NonNull};

use crate::handle::RawHandle;
use crate::state::GlobalState;
use crate::value::{RawTValue, TValue, TValueCursor};

#[repr(C)]
pub struct RawUpVal {
    pub tt: u8,
    pub marked: u8,
    pub memcat: u8,
    pub marked_open: u8,
    pub value: *mut RawTValue,
    pub data: RawUpValData,
}

#[repr(C)]
pub struct RawUpValOpen {
    pub prev: *mut RawUpVal,
    pub next: *mut RawUpVal,
    pub thread_next: *mut RawUpVal,
}

#[repr(C)]
pub union RawUpValData {
    pub value: ManuallyDrop<RawTValue>,
    pub open: ManuallyDrop<RawUpValOpen>,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
/// Non-owning identity of a VM upvalue record.
///
/// # Safety model for unsafe methods
///
/// The upvalue, its value slot, and any stack used for rebasing must remain
/// live in the same VM. Callers must preserve open-list ordering and closed
/// storage state while moving or closing it.
pub struct UpVal {
    raw: NonNull<RawUpVal>,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
/// Non-owning view of an open-upvalue list record.
///
/// Unsafe operations require a live open upvalue in the owning VM and valid
/// neighboring list links; mutation must preserve both open-upvalue lists.
pub struct UpValOpen {
    raw: NonNull<RawUpValOpen>,
}

#[allow(
    clippy::missing_safety_doc,
    reason = "UpVal's shared raw-handle contract is documented on UpVal"
)]
impl UpVal {
    pub const fn allocation_size() -> usize {
        size_of::<RawUpVal>()
    }

    pub const unsafe fn from_raw(raw: NonNull<RawUpVal>) -> Self {
        Self { raw }
    }

    pub unsafe fn from_ref(raw: &RawUpVal) -> Self {
        Self {
            raw: NonNull::from(raw),
        }
    }

    pub unsafe fn open_data(&self) -> UpValOpen {
        unsafe {
            UpValOpen::from_raw(NonNull::new_unchecked(
                (&raw mut (*self.as_ptr()).data.open).cast::<RawUpValOpen>(),
            ))
        }
    }

    pub unsafe fn value_ptr(&self) -> *mut RawTValue {
        unsafe { self.as_ptr().as_ref().unwrap_unchecked().value }
    }

    pub unsafe fn value(&self) -> TValue {
        unsafe { TValue::from_raw(NonNull::new_unchecked(self.value_ptr())) }
    }

    pub unsafe fn closed_value(&self) -> TValue {
        unsafe {
            TValue::from_raw(NonNull::new_unchecked(
                (&raw mut (*self.as_ptr()).data.value).cast::<RawTValue>(),
            ))
        }
    }

    pub unsafe fn set_value(&self, value: TValue) {
        unsafe {
            (*self.as_ptr()).value = value.as_ptr();
        }
    }

    pub unsafe fn rebase_value(&self, old_stack: TValueCursor, new_stack: TValueCursor) {
        debug_assert!(unsafe { self.is_open() });

        unsafe {
            let value = TValueCursor::from_ptr(self.value_ptr());
            let value_offset = value.addr_offset_from(old_stack) as usize;
            (*self.as_ptr()).value = new_stack.add(value_offset).as_ptr();
        }
    }

    pub unsafe fn close(&self) {
        unsafe {
            let closed_value = self.closed_value();
            closed_value.set_obj(self.value());
            self.set_value(closed_value);
        }
    }

    pub unsafe fn is_open(&self) -> bool {
        let closed_value = unsafe { (&raw const (*self.as_ptr()).data.value).cast::<RawTValue>() };
        !core::ptr::eq(unsafe { self.value_ptr() }.cast_const(), closed_value)
    }
}

#[allow(
    clippy::missing_safety_doc,
    reason = "UpValOpen's shared raw-view contract is documented on UpValOpen"
)]
impl UpValOpen {
    pub const unsafe fn from_raw(raw: NonNull<RawUpValOpen>) -> Self {
        Self { raw }
    }

    pub unsafe fn prev(&self) -> UpVal {
        unsafe {
            UpVal::from_raw(NonNull::new_unchecked(
                self.as_ptr().as_ref().unwrap_unchecked().prev,
            ))
        }
    }

    pub unsafe fn set_prev(&self, prev: UpVal) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().prev = prev.as_ptr();
        }
    }

    pub unsafe fn next(&self) -> UpVal {
        unsafe {
            UpVal::from_raw(NonNull::new_unchecked(
                self.as_ptr().as_ref().unwrap_unchecked().next,
            ))
        }
    }

    pub unsafe fn set_next(&self, next: UpVal) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().next = next.as_ptr();
        }
    }

    pub unsafe fn thread_next(&self) -> Option<UpVal> {
        unsafe {
            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().thread_next)
                .map(|upvalue| UpVal::from_raw(upvalue))
        }
    }

    pub unsafe fn set_thread_next(&self, thread_next: Option<UpVal>) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().thread_next =
                thread_next.map_or(ptr::null_mut(), |upvalue| upvalue.as_ptr());
        }
    }
}

#[allow(
    clippy::missing_safety_doc,
    reason = "GlobalState's shared raw-handle contract is documented on GlobalState"
)]
impl GlobalState {
    pub unsafe fn uv_head(&self) -> UpVal {
        unsafe { UpVal::from_raw(NonNull::new_unchecked(&raw mut (*self.as_ptr()).uv_head)) }
    }
}
impl crate::handle::sealed::Sealed for UpVal {}
impl crate::handle::sealed::Sealed for UpValOpen {}
impl RawHandle for UpVal {
    type Raw = RawUpVal;

    fn as_ptr(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }
}

impl AsRef<UpVal> for UpVal {
    fn as_ref(&self) -> &UpVal {
        self
    }
}

impl RawHandle for UpValOpen {
    type Raw = RawUpValOpen;

    fn as_ptr(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }
}

impl AsRef<UpValOpen> for UpValOpen {
    fn as_ref(&self) -> &UpValOpen {
        self
    }
}