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}