luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
use luau_common::BString;
use luau_compiler::{CompileOptions, CompilerError, StaticCompileHost, compile_with, dump_with};
use luau_syntax::parser::ParseOptions;

use super::Lua;
use crate::vector::Vector;

/// A constant value known to the Luau compiler.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum CompileConstant {
    /// The Luau `nil` value.
    Nil,
    /// A boolean.
    Boolean(bool),
    /// A floating-point number.
    Number(f64),
    /// An integer.
    Integer(i64),
    /// A vector.
    Vector(Vector),
    /// A byte string.
    String(BString),
}

impl CompileConstant {
    fn into_library_member_constant(self) -> luau_compiler::LibraryMemberConstant {
        match self {
            Self::Nil => luau_compiler::LibraryMemberConstant::Nil,
            Self::Boolean(value) => luau_compiler::LibraryMemberConstant::Boolean(value),
            Self::Number(value) => luau_compiler::LibraryMemberConstant::Number(value),
            Self::Integer(value) => luau_compiler::LibraryMemberConstant::Integer(value),
            Self::Vector(value) => {
                luau_compiler::LibraryMemberConstant::Vector(vector_constant(value))
            }
            Self::String(value) => luau_compiler::LibraryMemberConstant::String(value),
        }
    }
}

impl From<bool> for CompileConstant {
    fn from(value: bool) -> Self {
        Self::Boolean(value)
    }
}

impl From<f64> for CompileConstant {
    fn from(value: f64) -> Self {
        Self::Number(value)
    }
}

impl From<i32> for CompileConstant {
    fn from(value: i32) -> Self {
        Self::Number(f64::from(value))
    }
}

impl From<i64> for CompileConstant {
    fn from(value: i64) -> Self {
        Self::Integer(value)
    }
}

impl From<Vector> for CompileConstant {
    fn from(value: Vector) -> Self {
        Self::Vector(value)
    }
}

impl From<&str> for CompileConstant {
    fn from(value: &str) -> Self {
        Self::String(value.into())
    }
}

impl From<String> for CompileConstant {
    fn from(value: String) -> Self {
        Self::String(value.into())
    }
}

impl From<BString> for CompileConstant {
    fn from(value: BString) -> Self {
        Self::String(value)
    }
}

/// Configuration for compiling Luau source code.
#[derive(Debug, Clone, PartialEq)]
pub struct Compiler {
    options: CompileOptions,
    host: StaticCompileHost,
}

impl Compiler {
    /// Creates a compiler with Luau's default options.
    pub fn new() -> Self {
        Self {
            options: CompileOptions::default(),
            host: StaticCompileHost::default(),
        }
    }

    /// Sets the optimization level.
    ///
    /// Level 0 disables optimization, level 1 preserves debuggability, and
    /// level 2 enables aggressive optimization such as inlining.
    #[must_use]
    pub fn set_optimization_level(mut self, level: u8) -> Self {
        self.options.optimization_level = level;
        self
    }

    /// Sets the debug information level.
    ///
    /// Level 0 emits no debug information, level 1 emits line and function
    /// information, and level 2 also emits local and upvalue names.
    #[must_use]
    pub fn set_debug_level(mut self, level: u8) -> Self {
        self.options.debug_level = level;
        self
    }

    /// Sets the type information level used by native code generation.
    ///
    /// Level 0 emits type information for native modules and level 1 emits it
    /// for all modules.
    #[must_use]
    pub fn set_type_info_level(mut self, level: u8) -> Self {
        self.options.type_info_level = level;
        self
    }

    /// Sets the code coverage level.
    ///
    /// Level 0 disables coverage, level 1 tracks statements, and level 2 also
    /// tracks expressions.
    #[must_use]
    pub fn set_coverage_level(mut self, level: u8) -> Self {
        self.options.coverage_level = level;
        self
    }

    /// Sets an additional global vector constructor.
    ///
    /// Use `library.constructor` to name a constructor inside a library.
    #[must_use]
    pub fn set_vector_ctor(mut self, constructor: impl AsRef<str>) -> Self {
        let constructor = constructor.as_ref();
        if let Some((library, constructor)) = constructor.split_once('.') {
            self.host = self
                .host
                .with_member_vector_constructor(library.as_bytes(), constructor.as_bytes());
        } else {
            self.host = self
                .host
                .with_global_vector_constructor(constructor.as_bytes());
        }
        self
    }

    /// Sets an additional vector type name for type information.
    #[must_use]
    pub fn set_vector_type(mut self, type_name: impl Into<BString>) -> Self {
        self.host = self.host.with_vector_type_name(type_name);
        self
    }

    /// Adds a mutable global.
    ///
    /// Fields accessed through the global are excluded from import
    /// optimization.
    #[must_use]
    pub fn add_mutable_global(mut self, global: impl Into<BString>) -> Self {
        self.options.mutable_globals.push(global.into());
        self
    }

    /// Sets the mutable globals.
    ///
    /// Fields accessed through these globals are excluded from import
    /// optimization.
    #[must_use]
    pub fn set_mutable_globals<I, S>(mut self, globals: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<BString>,
    {
        self.options.mutable_globals = globals.into_iter().map(Into::into).collect();
        self
    }

    /// Adds a userdata type to emitted type information.
    #[must_use]
    pub fn add_userdata_type(mut self, type_name: impl Into<BString>) -> Self {
        self.host = self.host.with_userdata_type(type_name);
        self
    }

    /// Sets the userdata types included in emitted type information.
    #[must_use]
    pub fn set_userdata_types<I, S>(mut self, type_names: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<BString>,
    {
        self.host = self.host.with_userdata_types(type_names);
        self
    }

    /// Prevents a library function from being compiled as a builtin fastcall.
    #[must_use]
    pub fn add_disabled_builtin(mut self, builtin: impl Into<BString>) -> Self {
        self.options.disabled_builtins.push(builtin.into());
        self
    }

    /// Sets the library functions that must not be compiled as builtin fastcalls.
    #[must_use]
    pub fn set_disabled_builtins<I, S>(mut self, builtins: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<BString>,
    {
        self.options.disabled_builtins = builtins.into_iter().map(Into::into).collect();
        self
    }

    /// Adds a known `library.member` constant.
    ///
    /// Known constants are used at optimization level 2.
    #[must_use]
    pub fn add_library_constant(
        mut self,
        name: impl AsRef<str>,
        constant: impl Into<CompileConstant>,
    ) -> Self {
        let Some((library, member)) = name.as_ref().split_once('.') else {
            return self;
        };
        self.host = self.host.with_library_member_constant(
            library.as_bytes(),
            member.as_bytes(),
            constant.into().into_library_member_constant(),
        );
        self
    }

    /// Compiles Luau source code into bytecode.
    pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>, CompilerError> {
        luau_common::flags::initialize_luau_flags_default();
        compile_with(
            source,
            self.options.clone(),
            ParseOptions::default(),
            &self.host,
        )
    }

    /// Compiles Luau source code into a textual bytecode dump.
    pub fn dump(&self, source: impl AsRef<[u8]>) -> Result<BString, CompilerError> {
        luau_common::flags::initialize_luau_flags_default();
        dump_with(
            source,
            self.options.clone(),
            ParseOptions::default(),
            &self.host,
        )
    }
}

impl Lua {
    /// Sets the default compiler used by [`Lua::load`](Lua::load).
    pub fn set_compiler(&self, compiler: Compiler) {
        self.runtime.set_compiler(compiler);
    }
}

impl Default for Compiler {
    fn default() -> Self {
        Self::new()
    }
}

fn vector_constant(value: Vector) -> [f32; 4] {
    #[cfg(not(feature = "vector4"))]
    {
        [value.x(), value.y(), value.z(), 0.0]
    }
    #[cfg(feature = "vector4")]
    {
        [value.x(), value.y(), value.z(), value.w()]
    }
}