luaur_web/functions/
check_script.rs1use crate::records::demo_config_resolver::DemoConfigResolver;
11use crate::records::demo_file_resolver::DemoFileResolver;
12use alloc::string::{String, ToString};
13use core::cell::RefCell;
14use core::ffi::{c_char, c_int, CStr};
15use std::ffi::CString;
16
17use luaur_analysis::enums::solver_mode::SolverMode;
18use luaur_analysis::functions::freeze::freeze;
19use luaur_analysis::functions::register_builtin_globals::register_builtin_globals;
20use luaur_analysis::functions::to_string_error::to_string_type_error;
21use luaur_analysis::functions::unfreeze::unfreeze;
22use luaur_analysis::records::frontend::Frontend;
23use luaur_analysis::records::frontend_options::FrontendOptions;
24
25thread_local! {
26 static FINAL_CHECK_RESULT: RefCell<Option<CString>> = const { RefCell::new(None) };
29}
30
31fn run_check(source: &str, use_new_solver: c_int) -> String {
34 let mut final_check_result = String::new();
35
36 let mut file_resolver = DemoFileResolver::new();
37 let mut config_resolver = DemoConfigResolver::demo_config_resolver();
38 let options = FrontendOptions::default();
39
40 let mut frontend = Frontend::frontend_file_resolver_config_resolver_frontend_options(
41 &mut file_resolver.base,
42 &mut config_resolver.base,
43 &options,
44 );
45 unsafe {
46 frontend.wire_self_pointers();
47 }
48
49 frontend.set_luau_solver_mode(if use_new_solver != 0 {
50 SolverMode::New
51 } else {
52 SolverMode::Old
53 });
54
55 let frontend_ptr = &mut frontend as *mut Frontend;
60 unsafe {
61 unfreeze((*frontend_ptr).globals.global_types_mut());
62 register_builtin_globals(&mut *frontend_ptr, &mut (*frontend_ptr).globals, false);
63 freeze((*frontend_ptr).globals.global_types_mut());
64 }
65
66 frontend.clear();
70 file_resolver.source.clear();
71
72 file_resolver
74 .source
75 .insert("main".to_string(), source.to_string());
76
77 let check_result =
79 frontend.check_module_name_optional_frontend_options(&"main".to_string(), None);
80
81 for err in &check_result.errors {
82 if !final_check_result.is_empty() {
83 final_check_result.push('\n');
84 }
85 final_check_result.push_str(&(err.location.begin.line + 1).to_string());
87 final_check_result.push_str(": ");
88 final_check_result.push_str(&to_string_type_error(err));
90 }
91
92 final_check_result
93}
94
95#[cfg_attr(not(test), no_mangle)]
99pub unsafe extern "C" fn check_script(
100 source: *const c_char,
101 use_new_solver: c_int,
102) -> *const c_char {
103 let source_str = if source.is_null() {
104 String::new()
105 } else {
106 core::str::from_utf8_unchecked(CStr::from_ptr(source).to_bytes()).to_string()
107 };
108
109 let final_check_result =
111 match std::panic::catch_unwind(move || run_check(&source_str, use_new_solver)) {
112 Ok(result) => result,
113 Err(payload) => panic_message(&payload),
114 };
115
116 if final_check_result.is_empty() {
117 FINAL_CHECK_RESULT.with(|r| *r.borrow_mut() = None);
118 return core::ptr::null();
119 }
120
121 FINAL_CHECK_RESULT.with(|r| {
122 let cstring = CString::new(final_check_result).unwrap_or_default();
123 let ptr = cstring.as_ptr();
124 *r.borrow_mut() = Some(cstring);
125 ptr
126 })
127}
128
129fn panic_message(payload: &(dyn core::any::Any + Send)) -> String {
132 if let Some(s) = payload.downcast_ref::<&str>() {
133 s.to_string()
134 } else if let Some(s) = payload.downcast_ref::<String>() {
135 s.clone()
136 } else {
137 "unknown error".to_string()
138 }
139}