luau-vm 0.732.0

Pure-Rust Luau virtual machine, garbage collector, and standard libraries
Documentation
use core::ptr::NonNull;

use crate::Table;
use crate::buffer::Buffer;
use crate::function::{Closure, Proto, UpVal};
use crate::handle::RawHandle;
use crate::state::ThreadState;
use crate::string::TString;
use crate::thread::Thread;
use crate::types::{
    LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TOBJECT, LUA_TPROTO, LUA_TSTRING, LUA_TTABLE,
    LUA_TTHREAD, LUA_TUPVALUE, LUA_TUSERDATA,
};
use crate::userdata::Userdata;
use crate::{Class, Object};

use crate::table::LuaNode;
use crate::value::TValue;

#[repr(C)]
pub struct RawGcObject {
    pub tt: u8,
    pub marked: u8,
    pub memcat: u8,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
/// Non-owning identity of a collectable VM record.
///
/// # Safety model for unsafe methods
///
/// The allocation must be live and correctly tagged for the requested
/// conversion in its owning VM. The handle does not root the object, and any
/// collection or destruction that frees it invalidates every copy.
pub struct GcObject {
    raw: NonNull<RawGcObject>,
}

/// Closed conversion from a generic GC object to a known VM object kind.
///
/// # Safety
///
/// Implementations must use the exact GC tag and representation for `Self`.
/// The source allocation must remain live and correctly tagged.
#[allow(
    clippy::missing_safety_doc,
    reason = "the sole method uses the unsafe trait's closed conversion contract"
)]
pub(crate) unsafe trait GcHandle: Into<GcObject> {
    unsafe fn from_gc_object(object: GcObject) -> Self;
}

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

    /// `luaC_init`
    pub unsafe fn init_header(&self, thread: &Thread, type_tag: u8) {
        unsafe {
            let raw = self.as_ptr().as_mut().unwrap_unchecked();
            raw.memcat = thread.as_ptr().as_ref().unwrap_unchecked().active_memcat;
            raw.marked = thread.global().white();
            raw.tt = type_tag;
        }
    }

    /// `gco2ts`
    pub unsafe fn to_string(&self) -> TString {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TSTRING as u8
        );
        unsafe { TString::from_raw(self.raw.cast()) }
    }

    /// `gco2u`
    pub unsafe fn to_userdata(&self) -> Userdata {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TUSERDATA as u8
        );
        unsafe { Userdata::from_raw(self.raw.cast()) }
    }

    /// `gco2cl`
    pub unsafe fn to_closure(&self) -> Closure {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TFUNCTION as u8
        );
        unsafe { Closure::from_raw(self.raw.cast()) }
    }

    /// `gco2h`
    pub unsafe fn to_table(&self) -> Table {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TTABLE as u8
        );
        unsafe { Table::from_raw(self.raw.cast()) }
    }

    /// `gco2p`
    pub unsafe fn to_proto(&self) -> Proto {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TPROTO as u8
        );
        unsafe { Proto::from_raw(self.raw.cast()) }
    }

    /// `gco2uv`
    pub unsafe fn to_upvalue(&self) -> UpVal {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TUPVALUE as u8
        );
        unsafe { UpVal::from_raw(self.raw.cast()) }
    }

    /// `gco2th`
    pub unsafe fn to_state(&self) -> Thread {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TTHREAD as u8
        );
        unsafe { Thread::from_raw(self.raw.cast()) }
    }

    /// `gco2buf`
    pub unsafe fn to_buffer(&self) -> Buffer {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TBUFFER as u8
        );
        unsafe { Buffer::from_raw(self.raw.cast()) }
    }

    /// `gco2class`
    pub unsafe fn to_class(&self) -> Class {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TCLASS as u8
        );
        unsafe { Class::from_raw(self.raw.cast()) }
    }

    /// `gco2object`
    pub unsafe fn to_object(&self) -> Object {
        debug_assert_eq!(
            unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt },
            LUA_TOBJECT as u8
        );
        unsafe { Object::from_raw(self.raw.cast()) }
    }
}

impl From<TValue> for GcObject {
    fn from(value: TValue) -> Self {
        value.gc_value()
    }
}

impl From<TString> for GcObject {
    fn from(value: TString) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Table> for GcObject {
    fn from(value: Table) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<LuaNode> for GcObject {
    fn from(value: LuaNode) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Proto> for GcObject {
    fn from(value: Proto) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Closure> for GcObject {
    fn from(value: Closure) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<UpVal> for GcObject {
    fn from(value: UpVal) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Userdata> for GcObject {
    fn from(value: Userdata) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Buffer> for GcObject {
    fn from(value: Buffer) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Thread> for GcObject {
    fn from(value: Thread) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<&Thread> for GcObject {
    fn from(value: &Thread) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Class> for GcObject {
    fn from(value: Class) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

impl From<Object> for GcObject {
    fn from(value: Object) -> Self {
        unsafe { Self::from_raw(NonNull::new_unchecked(value.as_ptr().cast())) }
    }
}

unsafe impl GcHandle for TString {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Table {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Proto {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Closure {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for UpVal {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Userdata {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Buffer {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Thread {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Class {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}

unsafe impl GcHandle for Object {
    unsafe fn from_gc_object(object: GcObject) -> Self {
        unsafe { Self::from_raw(object.raw.cast()) }
    }
}
impl crate::handle::sealed::Sealed for GcObject {}
impl RawHandle for GcObject {
    type Raw = RawGcObject;

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

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