luaur_repl_cli/functions/
repl_main.rs1use alloc::string::String;
8use alloc::vec::Vec;
9use core::ffi::{c_char, CStr};
10use core::sync::atomic::{AtomicBool, AtomicI32, Ordering};
11
12use luaur_cli_lib::functions::get_source_files::get_source_files;
13use luaur_cli_lib::functions::set_luau_flags_flags_alt_b::set_luau_flags_c_char;
14use luaur_code_gen::functions::is_supported::is_supported;
15use luaur_common::functions::assert_handler::assert_handler;
16
17use luaur_vm::functions::lua_close::lua_close;
18use luaur_vm::functions::lua_l_newstate::lua_l_newstate;
19use luaur_vm::records::lua_state::lua_State;
20
21use crate::functions::assertion_handler::assertion_handler;
22use crate::functions::copts::GlobalOptions;
23use crate::functions::counters_dump::counters_dump;
24use crate::functions::counters_init::counters_init;
25use crate::functions::coverage_dump::coverage_dump;
26use crate::functions::coverage_init::coverage_init;
27use crate::functions::display_help::display_help;
28use crate::functions::profiler_dump::profiler_dump;
29use crate::functions::profiler_start::profiler_start;
30use crate::functions::profiler_stop::profiler_stop;
31use crate::functions::run_file::run_file;
32use crate::functions::run_repl::run_repl;
33use crate::functions::setup_state::setup_state;
34
35static REPL_CODEGEN: AtomicBool = AtomicBool::new(false);
38static REPL_CODEGEN_COLD: AtomicBool = AtomicBool::new(false);
39static PROGRAM_ARGC: AtomicI32 = AtomicI32::new(0);
40static mut PROGRAM_ARGV: *mut *mut c_char = core::ptr::null_mut();
41
42pub fn repl_codegen_enabled() -> bool {
44 REPL_CODEGEN.load(Ordering::Relaxed)
45}
46
47pub fn repl_codegen_cold() -> bool {
49 REPL_CODEGEN_COLD.load(Ordering::Relaxed)
50}
51
52pub fn program_argc() -> i32 {
54 PROGRAM_ARGC.load(Ordering::Relaxed)
55}
56
57pub fn program_argv() -> *mut *mut c_char {
59 unsafe { PROGRAM_ARGV }
60}
61
62#[no_mangle]
65pub(crate) static mut globalOptions: GlobalOptions = GlobalOptions {
66 optimizationLevel: 1,
67 debugLevel: 1,
68};
69
70#[allow(non_snake_case)]
71pub fn repl_main(argc: i32, argv: *mut *mut c_char) -> i32 {
72 *assert_handler() = Some(assertion_handler_adapter);
74
75 let mut profile: i32 = 0;
78 let mut coverage = false;
79 let mut interactive = false;
80 let mut codegen_perf = false;
81 let mut counters = false;
82 let mut program_args = argc;
83
84 REPL_CODEGEN.store(false, Ordering::Relaxed);
86 REPL_CODEGEN_COLD.store(false, Ordering::Relaxed);
87 unsafe {
88 globalOptions = GlobalOptions {
89 optimizationLevel: 1,
90 debugLevel: 1,
91 };
92 }
93
94 let arg = |i: i32| -> String {
95 unsafe {
96 let p = *argv.add(i as usize);
97 CStr::from_ptr(p).to_string_lossy().into_owned()
98 }
99 };
100 let argv0 = arg(0);
101
102 let mut i = 1i32;
103 while i < argc {
104 let a = arg(i);
105
106 if a == "-h" || a == "--help" {
107 display_help(&argv0);
108 return 0;
109 } else if a == "-i" || a == "--interactive" {
110 interactive = true;
111 } else if a.starts_with("-O") {
112 let level = atoi_like(&a[2..]);
114 if level < 0 || level > 2 {
115 eprintln!("Error: Optimization level must be between 0 and 2 inclusive.");
116 return 1;
117 }
118 unsafe {
119 globalOptions.optimizationLevel = level;
120 }
121 } else if a.starts_with("-g") {
122 let level = atoi_like(&a[2..]);
123 if level < 0 || level > 2 {
124 eprintln!("Error: Debug level must be between 0 and 2 inclusive.");
125 return 1;
126 }
127 unsafe {
128 globalOptions.debugLevel = level;
129 }
130 } else if a == "--profile" {
131 profile = 10000; } else if let Some(rest) = a.strip_prefix("--profile=") {
133 profile = atoi_like(rest);
134 } else if a == "--codegen" {
135 REPL_CODEGEN.store(true, Ordering::Relaxed);
136 } else if a == "--codegen-cold" {
137 REPL_CODEGEN.store(true, Ordering::Relaxed);
138 REPL_CODEGEN_COLD.store(true, Ordering::Relaxed);
139 } else if a == "--codegen-perf" {
140 REPL_CODEGEN.store(true, Ordering::Relaxed);
141 codegen_perf = true;
142 } else if a == "--coverage" {
143 coverage = true;
144 } else if a == "--counters" {
145 counters = true;
146 } else if a == "--timetrace" {
147 luaur_common::FFlag::DebugLuauTimeTracing.set(true);
148 } else if a.starts_with("--fflags=") {
149 unsafe {
151 let p = *argv.add(i as usize);
152 set_luau_flags_c_char(p.add(9));
153 }
154 } else if a == "--program-args" || a == "-a" {
155 program_args = i + 1;
156 break;
157 } else if a.starts_with('-') {
158 eprintln!("Error: Unrecognized option '{}'.\n", a);
159 display_help(&argv0);
160 return 1;
161 }
162
163 i += 1;
164 }
165
166 PROGRAM_ARGC.store(argc - program_args, Ordering::Relaxed);
167 unsafe {
168 PROGRAM_ARGV = argv.add(program_args as usize);
169 }
170
171 if luaur_common::FFlag::DebugLuauTimeTracing.get() {
173 eprintln!(
174 "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled"
175 );
176 return 1;
177 }
178
179 if codegen_perf {
180 eprintln!("--codegen-perf option is only supported on Linux");
184 return 1;
185 }
186
187 if repl_codegen_enabled() && !is_supported() {
188 eprintln!("Warning: Native code generation is not supported in current configuration");
189 }
190
191 let files: Vec<String> = get_source_files(argc, argv);
192
193 if files.is_empty() {
194 unsafe {
195 run_repl();
196 }
197 0
198 } else {
199 unsafe {
200 let l: *mut lua_State = lua_l_newstate();
201
202 setup_state(l);
203
204 if profile != 0 {
205 profiler_start(l, profile);
206 }
207
208 if coverage {
209 coverage_init(l);
210 }
211
212 if counters {
213 counters_init(l as *mut core::ffi::c_void);
214 }
215
216 let mut failed = 0i32;
217
218 let n = files.len();
219 for (idx, file) in files.iter().enumerate() {
220 let is_last_file = idx == n - 1;
221 let ran = run_file(file, l, interactive && is_last_file);
222 failed += (!ran) as i32;
223 }
224
225 if profile != 0 {
226 profiler_stop();
227 profiler_dump(c"profile.out".as_ptr());
228 }
229
230 if coverage {
231 coverage_dump("coverage.out");
232 }
233
234 if counters {
235 counters_dump("callgrind.out");
236 }
237
238 lua_close(l);
239
240 if failed != 0 {
241 1
242 } else {
243 0
244 }
245 }
246 }
247}
248
249unsafe extern "C" fn assertion_handler_adapter(
251 expr: *const c_char,
252 file: *const c_char,
253 line: i32,
254 function: *const c_char,
255) -> i32 {
256 assertion_handler(expr, file, line, function)
257}
258
259fn atoi_like(s: &str) -> i32 {
262 let bytes = s.as_bytes();
263 let mut idx = 0;
264 let mut sign = 1i32;
265
266 if idx < bytes.len() && (bytes[idx] == b'+' || bytes[idx] == b'-') {
267 if bytes[idx] == b'-' {
268 sign = -1;
269 }
270 idx += 1;
271 }
272
273 let mut value: i32 = 0;
274 while idx < bytes.len() && bytes[idx].is_ascii_digit() {
275 value = value
276 .wrapping_mul(10)
277 .wrapping_add((bytes[idx] - b'0') as i32);
278 idx += 1;
279 }
280
281 sign * value
282}