luau-vm 0.732.0

Pure-Rust Luau virtual machine, garbage collector, and standard libraries
Documentation
mod call_info;
mod callbacks;
mod lifecycle;
mod thread;

pub(crate) use crate::memory::allocator::VmAllocator;
pub use crate::memory::allocator::{LuaAllocator, SystemLuaAllocator, VM_ALLOC_ALIGN};
pub use call_info::{CallInfo, CallInfoCursor, RawCallInfo, RawCallInfoSaved};
pub use callbacks::{
    AllocateCallback, EmbedderGc, EmbedderMark, ExecutionCallbackStorage, ExecutionClose,
    ExecutionCounterData, ExecutionDestroy, ExecutionDisable, ExecutionEnter,
    ExecutionInlineFunction, ExecutionInterrupt, ExecutionMemorySize, ExecutionTypeMapping,
    GcInterrupt, GcInterruptCallback, GcPhase, InterruptKind, InterruptRequest, LuaCallbacks,
    LuaExecutionCallbacks, PatternInterrupt, ProtectedErrorAction, ProtectedErrorCallback,
    UserAtomCallback, UserThreadCallback,
};
pub use lifecycle::ThreadLifecycle;
pub(crate) use lifecycle::open_main_state;
pub use thread::{RawLuaState, ThreadState};

use core::cell::UnsafeCell;
use core::ptr::NonNull;

use crate::call::LuaProtectedErrorFrame;
use crate::function::RawUpVal;
use crate::gc::{GcStats, RawGcObject};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::memory::{LUA_MEMORY_CATEGORIES, LUA_SIZE_CLASSES, RawLuaPage};
use crate::metamethod::TM_N;
use crate::string::{RawTString, StringTable};
use crate::table::RawLuaTable;
use crate::thread::{LUA_MIN_STACK, LUAI_MAX_NATIVE_CALLS, Thread};
use crate::types::LUA_T_COUNT;
use crate::userdata::{LuaUserdataDirectAccessData, UserdataTypeRegistry};
use crate::value::{RawTValue, TValue};

#[repr(C)]
pub struct RawMainState {
    pub state: RawLuaState,
    pub global: RawGlobalState,
}

#[repr(C)]
pub struct RawGlobalState {
    pub string_table: StringTable,
    pub(crate) allocator: NonNull<VmAllocator>,
    pub current_white: u8,
    pub gc_state: u8,
    pub gray: *mut RawGcObject,
    pub gray_again: *mut RawGcObject,
    pub weak: *mut RawGcObject,
    pub gc_threshold: usize,
    pub total_bytes: usize,
    pub gc_goal: i32,
    pub gc_step_mul: i32,
    pub gc_step_size: i32,
    pub free_pages: [*mut RawLuaPage; LUA_SIZE_CLASSES],
    pub free_gco_pages: [*mut RawLuaPage; LUA_SIZE_CLASSES],
    pub all_pages: *mut RawLuaPage,
    pub all_gco_pages: *mut RawLuaPage,
    pub sweep_gco_page: *mut RawLuaPage,
    pub main_thread: *mut RawLuaState,
    pub uv_head: RawUpVal,
    pub mt: [*mut RawLuaTable; LUA_T_COUNT],
    pub tt_name: [*mut RawTString; LUA_T_COUNT],
    pub tm_name: [*mut RawTString; TM_N],
    pub pseudo_temp: RawTValue,
    pub registry: RawTValue,
    pub registry_free: i32,
    pub protected_error: *mut LuaProtectedErrorFrame,
    pub rng_state: u64,
    pub ptr_enc_key: [u64; 4],
    pub cb: LuaCallbacks,
    pub ecb: LuaExecutionCallbacks,
    pub ecb_data: ExecutionCallbackStorage,
    pub(crate) userdata_type_registry: UnsafeCell<UserdataTypeRegistry>,
    pub userdata_direct: [LuaUserdataDirectAccessData; crate::userdata::USERDATA_INTERNAL_LIMIT],
    pub memcat_bytes: [usize; LUA_MEMORY_CATEGORIES],
    pub userdata_gc: [Option<crate::userdata::LuaDestructor>; crate::userdata::USERDATA_TAG_LIMIT],
    pub userdata_mark:
        [Option<crate::userdata::LuaUserdataMark>; crate::userdata::USERDATA_TAG_LIMIT],
    pub userdata_mt: [*mut RawLuaTable; crate::userdata::USERDATA_TAG_LIMIT],
    pub weak_registry: RawTValue,
    pub weak_registry_free: i32,
    pub embedder_gc: Option<EmbedderGc>,
    pub light_userdata_name: [*mut RawTString; crate::userdata::LIGHT_USERDATA_TAG_LIMIT],
    pub userdata_direct_fields: [*mut RawLuaTable; crate::userdata::USERDATA_INTERNAL_LIMIT],
    pub gc_stats: GcStats,
    pub last_proto_id: u32,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
/// Non-owning identity of a VM's global state record.
///
/// # Safety model for unsafe methods
///
/// The owning VM must remain live. Returned threads, objects, pointers, and
/// record views are non-owning and must not outlive their storage. Mutation
/// must be serialized with VM execution and preserve the owning subsystem's
/// invariants.
pub struct GlobalState {
    raw: NonNull<RawGlobalState>,
}

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

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

    pub fn encode_pointer(&self, pointer: usize) -> usize {
        let keys = unsafe { self.as_ptr().as_ref().unwrap_unchecked().ptr_enc_key };

        ((keys[0] as usize)
            .wrapping_mul(pointer)
            .wrapping_add(keys[2] as usize))
            ^ ((keys[1] as usize)
                .wrapping_mul(pointer)
                .wrapping_add(keys[3] as usize))
    }

    pub fn registry(&self) -> TValue {
        unsafe { TValue::from_raw(NonNull::new_unchecked(&raw mut (*self.as_ptr()).registry)) }
    }

    pub fn pseudo_temp(&self) -> TValue {
        unsafe {
            TValue::from_raw(NonNull::new_unchecked(
                &raw mut (*self.as_ptr()).pseudo_temp,
            ))
        }
    }

    pub fn registry_free(&self) -> i32 {
        unsafe { (*self.as_ptr()).registry_free }
    }

    pub fn set_registry_free(&self, registry_free: i32) {
        unsafe {
            (*self.as_ptr()).registry_free = registry_free;
        }
    }

    pub(crate) fn weak_registry(&self) -> TValue {
        unsafe {
            TValue::from_raw(NonNull::new_unchecked(
                &raw mut (*self.as_ptr()).weak_registry,
            ))
        }
    }

    pub(crate) fn weak_registry_free(&self) -> i32 {
        unsafe { (*self.as_ptr()).weak_registry_free }
    }

    pub(crate) fn set_weak_registry_free(&self, registry_free: i32) {
        unsafe {
            (*self.as_ptr()).weak_registry_free = registry_free;
        }
    }

    pub(crate) fn embedder_gc(&self) -> Option<EmbedderGc> {
        unsafe { (*self.as_ptr()).embedder_gc }
    }

    pub(crate) fn set_embedder_gc(&self, callback: Option<EmbedderGc>) {
        unsafe {
            (*self.as_ptr()).embedder_gc = callback;
        }
    }
}

impl Sealed for GlobalState {}

impl RawHandle for GlobalState {
    type Raw = RawGlobalState;

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

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

pub(crate) const THREAD_STATUS_OK: u8 = 0;
pub(crate) const THREAD_STATUS_YIELD: u8 = 1;
pub(crate) const THREAD_STATUS_ERR_RUN: u8 = 2;
pub(crate) const THREAD_STATUS_ERR_SYNTAX: u8 = 3;
pub(crate) const THREAD_STATUS_ERR_MEM: u8 = 4;
pub(crate) const THREAD_STATUS_ERR_ERR: u8 = 5;
pub(crate) const THREAD_STATUS_BREAK: u8 = 6;
pub(crate) const THREAD_STATUS_SCHEDULED_REENTRY: u8 = 0x7f;

pub const LUA_MEMERRMSG: &[u8] = b"not enough memory";
pub const LUA_ERRERRMSG: &[u8] = b"error in error handling";

/// `LUA_EXECUTION_CALLBACK_STORAGE`
pub(crate) const LUA_EXECUTION_CALLBACK_STORAGE: usize = 512;

pub const EXTRA_STACK: usize = 5;
pub const BASIC_CI_SIZE: usize = 8;
pub const BASIC_STACK_SIZE: usize = 2 * LUA_MIN_STACK;
pub const INITIAL_STACK_SIZE: usize = BASIC_STACK_SIZE + EXTRA_STACK;
pub const MAX_NATIVE_CALLS_HARD: u16 = LUAI_MAX_NATIVE_CALLS + (LUAI_MAX_NATIVE_CALLS >> 3);
pub const LUA_CALLINFO_RETURN: u32 = 1 << 0;
pub const LUA_CALLINFO_HANDLE: u32 = 1 << 1;
pub const LUA_CALLINFO_NATIVE: u32 = 1 << 2;
pub const LUA_CALLINFO_OP_YIELD: u32 = 1 << 3;

pub const LUA_OK: i32 = THREAD_STATUS_OK as i32;
pub const LUA_YIELD: i32 = THREAD_STATUS_YIELD as i32;
pub const LUA_ERRRUN: i32 = THREAD_STATUS_ERR_RUN as i32;
pub const LUA_ERRSYNTAX: i32 = THREAD_STATUS_ERR_SYNTAX as i32;
pub const LUA_ERRMEM: i32 = THREAD_STATUS_ERR_MEM as i32;
pub const LUA_ERRERR: i32 = THREAD_STATUS_ERR_ERR as i32;
pub const LUA_BREAK: i32 = THREAD_STATUS_BREAK as i32;