Skip to main content

luaur_rt/
compiler.rs

1//! The [`Compiler`] builder. Mirrors `mlua::Compiler`.
2//!
3//! Luau compiles *source* to bytecode through a set of compile-time options
4//! (optimization level, debug level, the `vector` library/ctor/type names used
5//! to enable vector fastcalls, the set of globals treated as mutable, etc.).
6//! mlua exposes these as a fluent `Compiler` builder; this is the luaur-rt
7//! equivalent, built directly on `luaur_compiler`'s [`CompileOptions`].
8//!
9//! A [`Compiler`] owns the C strings backing the pointer fields of
10//! `CompileOptions` (the VM reads raw `*const c_char` pointers), so it must be
11//! kept alive while a chunk built with it is compiled. [`Chunk::set_compiler`]
12//! / [`Lua::set_compiler`] store it for exactly that scope.
13
14use std::ffi::CString;
15
16use luaur_compiler::records::compile_options::CompileOptions;
17
18/// Luau bytecode compiler options, mirroring `mlua::Compiler`.
19///
20/// Construct with [`Compiler::new`], tune with the `set_*` builder methods, and
21/// attach to a chunk with [`Chunk::set_compiler`](crate::Chunk::set_compiler)
22/// (or to the whole VM with [`Lua::set_compiler`](crate::Lua::set_compiler)).
23///
24/// ```
25/// use luaur_rt::Compiler;
26/// let _c = Compiler::new()
27///     .set_optimization_level(2)
28///     .set_debug_level(1)
29///     .set_vector_ctor("vector");
30/// ```
31#[derive(Debug, Clone)]
32pub struct Compiler {
33    optimization_level: u8,
34    debug_level: u8,
35    type_info_level: u8,
36    coverage_level: u8,
37    vector_lib: Option<CString>,
38    vector_ctor: Option<CString>,
39    vector_type: Option<CString>,
40    mutable_globals: Vec<CString>,
41}
42
43impl Default for Compiler {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl Compiler {
50    /// Create a `Compiler` with Luau's default options (optimization level 1,
51    /// debug level 1). Mirrors `mlua::Compiler::new`.
52    pub fn new() -> Self {
53        let defaults = CompileOptions::default();
54        Compiler {
55            optimization_level: defaults.optimization_level as u8,
56            debug_level: defaults.debug_level as u8,
57            type_info_level: defaults.type_info_level as u8,
58            coverage_level: defaults.coverage_level as u8,
59            vector_lib: None,
60            vector_ctor: None,
61            vector_type: None,
62            mutable_globals: Vec::new(),
63        }
64    }
65
66    /// Set the optimization level (0..=2). Mirrors
67    /// `mlua::Compiler::set_optimization_level`.
68    pub fn set_optimization_level(mut self, level: u8) -> Self {
69        self.optimization_level = level;
70        self
71    }
72
73    /// Set the debug level (0..=2). Mirrors `mlua::Compiler::set_debug_level`.
74    pub fn set_debug_level(mut self, level: u8) -> Self {
75        self.debug_level = level;
76        self
77    }
78
79    /// Set the type-info level. Mirrors `mlua::Compiler::set_type_info_level`.
80    pub fn set_type_info_level(mut self, level: u8) -> Self {
81        self.type_info_level = level;
82        self
83    }
84
85    /// Set the coverage level (0..=2). Mirrors
86    /// `mlua::Compiler::set_coverage_level`.
87    pub fn set_coverage_level(mut self, level: u8) -> Self {
88        self.coverage_level = level;
89        self
90    }
91
92    /// Set the library name whose members are recognized as vector operations
93    /// (enables vector fastcalls). Mirrors `mlua::Compiler::set_vector_lib`.
94    pub fn set_vector_lib(mut self, lib: impl Into<Vec<u8>>) -> Self {
95        self.vector_lib = CString::new(lib).ok();
96        self
97    }
98
99    /// Set the constructor name used to build vectors (enables compiling
100    /// `vector(...)` calls to the `vector` type). Mirrors
101    /// `mlua::Compiler::set_vector_ctor`.
102    pub fn set_vector_ctor(mut self, ctor: impl Into<Vec<u8>>) -> Self {
103        self.vector_ctor = CString::new(ctor).ok();
104        self
105    }
106
107    /// Set the user vector *type* name used for field/method fastcalls.
108    /// Mirrors `mlua::Compiler::set_vector_type`.
109    pub fn set_vector_type(mut self, ty: impl Into<Vec<u8>>) -> Self {
110        self.vector_type = CString::new(ty).ok();
111        self
112    }
113
114    /// Set the list of globals the compiler is allowed to treat as mutable
115    /// (so it won't constant-fold reads of them). Mirrors
116    /// `mlua::Compiler::set_mutable_globals`.
117    pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
118        self.mutable_globals = globals
119            .into_iter()
120            .filter_map(|g| CString::new(g).ok())
121            .collect();
122        self
123    }
124
125    /// Build a [`CompileOptions`] view over `self`. The returned options borrow
126    /// the C strings owned by `self`, so `self` must outlive the compile call.
127    ///
128    /// The `null`-terminated pointer array for `mutable_globals` is built into
129    /// `scratch`, which must also outlive the returned options.
130    pub(crate) fn to_options<'a>(
131        &'a self,
132        scratch: &'a mut Vec<*const core::ffi::c_char>,
133    ) -> CompileOptions {
134        let mut options = CompileOptions::default();
135        options.optimization_level = self.optimization_level as core::ffi::c_int;
136        options.debug_level = self.debug_level as core::ffi::c_int;
137        options.type_info_level = self.type_info_level as core::ffi::c_int;
138        options.coverage_level = self.coverage_level as core::ffi::c_int;
139        if let Some(s) = &self.vector_lib {
140            options.vector_lib = s.as_ptr();
141        }
142        if let Some(s) = &self.vector_ctor {
143            options.vector_ctor = s.as_ptr();
144        }
145        if let Some(s) = &self.vector_type {
146            options.vector_type = s.as_ptr();
147        }
148        if !self.mutable_globals.is_empty() {
149            scratch.clear();
150            for g in &self.mutable_globals {
151                scratch.push(g.as_ptr());
152            }
153            scratch.push(core::ptr::null());
154            options.mutable_globals = scratch.as_ptr();
155        }
156        options
157    }
158}