luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
use std::rc::Rc;

use luau_vm::lua::Lua as VmLua;
use luau_vm::state::{LuaAllocator, SystemLuaAllocator};

use crate::error::Error;
use crate::hooks::CallbackManager;
use crate::lua::runtime::RuntimeData;
use crate::thread::Thread;
use luau_vm::Thread as VmThread;

mod app_data;
mod chunk;
mod compiler;
mod debug;
mod gc;
mod memory;
mod reference;
mod registry;
pub(crate) mod runtime;
mod sandbox;
mod scope;
mod stdlib;

use memory::{MemoryState, TrackingAllocator};
pub(crate) use sandbox::ChunkLoad;

pub use app_data::{AppDataRef, AppDataRefMut};
pub use chunk::{AsChunk, Chunk, ChunkMode, SandboxedChunk};
#[cfg(feature = "macros")]
#[doc(hidden)]
pub use chunk::{CaptureEnvironment, CapturedChunk, captured_chunk};
pub use compiler::{CompileConstant, Compiler};
pub use debug::StackInfo;
pub use reference::LuaRef;
pub use registry::RegistryKey;
pub(crate) use registry::RegistryState;
pub use scope::Scope;
pub use stdlib::StdLib;

/// An owned Luau virtual machine.
///
/// Values created by this state borrow it and cannot outlive it.
pub struct Lua {
    state: VmLua,
    pub(crate) runtime: Box<RuntimeData>,
}

impl Lua {
    /// Creates a Luau state with the system allocator and all standard libraries.
    pub fn new() -> Result<Self, Error> {
        Self::new_with_libs(StdLib::ALL)
    }

    /// Creates a Luau state with the system allocator and the selected libraries.
    pub fn new_with_libs(libs: StdLib) -> Result<Self, Error> {
        luau_common::flags::initialize_luau_flags_default();
        let memory = Rc::new(MemoryState::default());
        let state = VmLua::new_with_allocator(TrackingAllocator::new(
            SystemLuaAllocator,
            Rc::clone(&memory),
        ))
        .ok_or_else(|| Error::MemoryError("failed to allocate Luau state".to_string()))?;
        let lua = Self::from_vm(state, memory);
        lua.load_std_libs(libs)?;
        Ok(lua)
    }

    /// Creates a Luau state with a custom allocator and all standard libraries.
    pub fn new_with_allocator<A: LuaAllocator + 'static>(allocator: A) -> Result<Self, Error> {
        Self::new_with(allocator, StdLib::ALL)
    }

    /// Creates a Luau state with a custom allocator and the selected libraries.
    pub fn new_with<A: LuaAllocator + 'static>(allocator: A, libs: StdLib) -> Result<Self, Error> {
        luau_common::flags::initialize_luau_flags_default();
        let memory = Rc::new(MemoryState::default());
        let state =
            VmLua::new_with_allocator(TrackingAllocator::new(allocator, Rc::clone(&memory)))
                .ok_or_else(|| Error::MemoryError("failed to allocate Luau state".to_string()))?;
        let lua = Self::from_vm(state, memory);
        lua.load_std_libs(libs)?;
        Ok(lua)
    }

    fn from_vm(state: VmLua, memory: Rc<MemoryState>) -> Self {
        let runtime = Box::new(RuntimeData::new(memory, state.main_thread()));
        let mut lua = Self { state, runtime };
        lua.install_runtime_data();
        lua
    }

    /// Returns a handle to the main Luau thread.
    pub fn current_thread(&self) -> Thread<'_> {
        Thread::new(self.state.main_thread(), &self.runtime)
    }

    pub(crate) fn lua_ref(&self) -> LuaRef<'_> {
        LuaRef::new(self.state.main_thread(), &self.runtime)
    }

    pub(crate) fn callback_parts(&mut self) -> (&VmThread, &mut CallbackManager) {
        (self.state.main_thread(), self.runtime.callbacks_mut())
    }

    fn install_runtime_data(&mut self) {
        unsafe {
            self.runtime
                .install_callbacks(&mut *self.state.main_thread().callbacks());
        }
    }
}

impl Drop for Lua {
    fn drop(&mut self) {
        self.runtime
            .userdata_registrations()
            .close(self.state.main_thread());
        self.runtime.registry().close(self.state.main_thread());
    }
}