luau-vm 0.732.0

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

use crate::Table;
use crate::function::{Closure, RawUpVal, UpVal};
use crate::gc::{GcObject, RawGcObject};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::string::{RawTString, TString};
use crate::table::RawLuaTable;
use crate::thread::Thread;
use crate::value::{RawTValue, TValueCursor};

use super::{CallInfo, CallInfoCursor, RawCallInfo, RawGlobalState};

#[repr(C)]
pub struct RawLuaState {
    pub tt: u8,
    pub marked: u8,
    pub memcat: u8,
    pub status: u8,
    pub active_memcat: u8,
    pub is_active: bool,
    pub single_step: bool,
    pub top: *mut RawTValue,
    pub base: *mut RawTValue,
    pub global: *mut RawGlobalState,
    pub ci: *mut RawCallInfo,
    pub stack_last: *mut RawTValue,
    pub stack: *mut RawTValue,
    pub end_ci: *mut RawCallInfo,
    pub base_ci: *mut RawCallInfo,
    pub stack_size: i32,
    pub size_ci: i32,
    pub native_call_depth: u16,
    pub base_native_call_depth: u16,
    pub cached_slot: i32,
    pub gt: *mut RawLuaTable,
    pub open_upval: *mut RawUpVal,
    pub gc_list: *mut RawGcObject,
    pub name_call: *mut RawTString,
    pub userdata: *mut (),
}

/// Unstable access to a thread's raw execution state.
///
/// # Safety
///
/// The thread and owning VM must be live. Returned handles, pointers, record
/// views, and cursors are non-owning and are invalidated by their documented
/// stack, call-frame, GC, close, reset, or destruction transitions.
#[allow(
    clippy::missing_safety_doc,
    reason = "all methods share the capability-level safety contract"
)]
pub trait ThreadState: Sealed + RawHandle<Raw = RawLuaState> {
    unsafe fn from_raw(raw: NonNull<RawLuaState>) -> Self
    where
        Self: Sized;

    unsafe fn current_call_info_cursor(&self) -> CallInfoCursor;

    unsafe fn base_call_info_cursor(&self) -> CallInfoCursor;

    unsafe fn current_call_info(&self) -> CallInfo {
        unsafe { self.current_call_info_cursor().call_info_unchecked() }
    }

    unsafe fn base_call_info(&self) -> CallInfo {
        unsafe { self.base_call_info_cursor().call_info_unchecked() }
    }

    unsafe fn set_current_call_info(&self, call_info: CallInfoCursor);

    unsafe fn stack(&self) -> TValueCursor;

    unsafe fn stack_base(&self) -> TValueCursor;

    unsafe fn stack_top(&self) -> TValueCursor;

    unsafe fn stack_last(&self) -> TValueCursor;

    unsafe fn set_stack(&self, stack: TValueCursor);

    unsafe fn set_stack_base(&self, base: TValueCursor);

    unsafe fn set_stack_top(&self, top: TValueCursor);

    unsafe fn open_upvalue(&self) -> Option<UpVal>;

    unsafe fn set_open_upvalue(&self, upvalue: Option<UpVal>);

    unsafe fn gc_list(&self) -> Option<GcObject>;

    unsafe fn set_gc_list(&self, gc_list: Option<GcObject>);

    unsafe fn name_call(&self) -> Option<TString>;

    unsafe fn set_name_call(&self, name_call: Option<TString>);

    unsafe fn cached_slot(&self) -> i32;

    unsafe fn set_cached_slot(&self, cached_slot: i32);

    unsafe fn restore_call_frame(&self, call_info: CallInfoCursor, top: TValueCursor) {
        unsafe {
            self.set_current_call_info(call_info);
            self.set_stack_base(call_info.call_info_unchecked().base());
            self.set_stack_top(top);
        }
    }

    unsafe fn globals(&self) -> Table;

    unsafe fn set_globals(&self, globals: Table);

    unsafe fn increment_native_call_depth(&self);

    unsafe fn decrement_native_call_depth(&self);

    unsafe fn increment_base_native_call_depth(&self);

    unsafe fn decrement_base_native_call_depth(&self);

    unsafe fn current_function(&self) -> Closure {
        unsafe { self.current_call_info().function_closure() }
    }

    unsafe fn current_env(&self) -> Table {
        unsafe {
            if self.current_call_info() == self.base_call_info() {
                self.globals()
            } else {
                self.current_function().env()
            }
        }
    }
}

impl ThreadState for Thread {
    unsafe fn from_raw(raw: NonNull<RawLuaState>) -> Self {
        Self { raw }
    }

    unsafe fn current_call_info_cursor(&self) -> CallInfoCursor {
        unsafe { CallInfoCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().ci) }
    }

    unsafe fn base_call_info_cursor(&self) -> CallInfoCursor {
        unsafe { CallInfoCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().base_ci) }
    }

    unsafe fn set_current_call_info(&self, call_info: CallInfoCursor) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().ci = call_info.as_ptr();
        }
    }

    unsafe fn stack(&self) -> TValueCursor {
        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().stack) }
    }

    unsafe fn stack_base(&self) -> TValueCursor {
        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().base) }
    }

    unsafe fn stack_top(&self) -> TValueCursor {
        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().top) }
    }

    unsafe fn stack_last(&self) -> TValueCursor {
        unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().stack_last) }
    }

    unsafe fn set_stack(&self, stack: TValueCursor) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().stack = stack.as_ptr();
        }
    }

    unsafe fn set_stack_base(&self, base: TValueCursor) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().base = base.as_ptr();
        }
    }

    unsafe fn set_stack_top(&self, top: TValueCursor) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().top = top.as_ptr();
        }
    }

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

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

    unsafe fn gc_list(&self) -> Option<GcObject> {
        unsafe {
            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
                .map(|raw| GcObject::from_raw(raw))
        }
    }

    unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().gc_list =
                gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
        }
    }

    unsafe fn name_call(&self) -> Option<TString> {
        unsafe {
            NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().name_call)
                .map(|raw| TString::from_raw(raw))
        }
    }

    unsafe fn set_name_call(&self, name_call: Option<TString>) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().name_call =
                name_call.map_or(ptr::null_mut(), |name_call| name_call.as_ptr());
        }
    }

    unsafe fn cached_slot(&self) -> i32 {
        unsafe { self.as_ptr().as_ref().unwrap_unchecked().cached_slot }
    }

    unsafe fn set_cached_slot(&self, cached_slot: i32) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().cached_slot = cached_slot;
        }
    }

    unsafe fn globals(&self) -> Table {
        unsafe {
            debug_assert!(!self.as_ptr().as_ref().unwrap_unchecked().gt.is_null());
            Table::from_raw(NonNull::new_unchecked(
                self.as_ptr().as_ref().unwrap_unchecked().gt,
            ))
        }
    }

    unsafe fn set_globals(&self, globals: Table) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().gt = globals.as_ptr();
        }
    }

    unsafe fn increment_native_call_depth(&self) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().native_call_depth += 1;
        }
    }

    unsafe fn decrement_native_call_depth(&self) {
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().native_call_depth -= 1;
        }
    }

    unsafe fn increment_base_native_call_depth(&self) {
        unsafe {
            self.as_ptr()
                .as_mut()
                .unwrap_unchecked()
                .base_native_call_depth += 1;
        }
    }

    unsafe fn decrement_base_native_call_depth(&self) {
        unsafe {
            self.as_ptr()
                .as_mut()
                .unwrap_unchecked()
                .base_native_call_depth -= 1;
        }
    }
}