use crate::records::demo_config_resolver::DemoConfigResolver;
use crate::records::demo_file_resolver::DemoFileResolver;
use alloc::string::{String, ToString};
use core::cell::RefCell;
use core::ffi::{c_char, c_int, CStr};
use std::ffi::CString;
use luaur_analysis::enums::solver_mode::SolverMode;
use luaur_analysis::functions::freeze::freeze;
use luaur_analysis::functions::register_builtin_globals::register_builtin_globals;
use luaur_analysis::functions::to_string_error::to_string_type_error;
use luaur_analysis::functions::unfreeze::unfreeze;
use luaur_analysis::records::frontend::Frontend;
use luaur_analysis::records::frontend_options::FrontendOptions;
thread_local! {
static FINAL_CHECK_RESULT: RefCell<Option<CString>> = const { RefCell::new(None) };
}
fn run_check(source: &str, use_new_solver: c_int) -> String {
let mut final_check_result = String::new();
let mut file_resolver = DemoFileResolver::new();
let mut config_resolver = DemoConfigResolver::demo_config_resolver();
let options = FrontendOptions::default();
let mut frontend = Frontend::frontend_file_resolver_config_resolver_frontend_options(
&mut file_resolver.base,
&mut config_resolver.base,
&options,
);
unsafe {
frontend.wire_self_pointers();
}
frontend.set_luau_solver_mode(if use_new_solver != 0 {
SolverMode::New
} else {
SolverMode::Old
});
let frontend_ptr = &mut frontend as *mut Frontend;
unsafe {
unfreeze((*frontend_ptr).globals.global_types_mut());
register_builtin_globals(&mut *frontend_ptr, &mut (*frontend_ptr).globals, false);
freeze((*frontend_ptr).globals.global_types_mut());
}
frontend.clear();
file_resolver.source.clear();
file_resolver
.source
.insert("main".to_string(), source.to_string());
let check_result =
frontend.check_module_name_optional_frontend_options(&"main".to_string(), None);
for err in &check_result.errors {
if !final_check_result.is_empty() {
final_check_result.push('\n');
}
final_check_result.push_str(&(err.location.begin.line + 1).to_string());
final_check_result.push_str(": ");
final_check_result.push_str(&to_string_type_error(err));
}
final_check_result
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn check_script(
source: *const c_char,
use_new_solver: c_int,
) -> *const c_char {
let source_str = if source.is_null() {
String::new()
} else {
core::str::from_utf8_unchecked(CStr::from_ptr(source).to_bytes()).to_string()
};
let final_check_result =
match std::panic::catch_unwind(move || run_check(&source_str, use_new_solver)) {
Ok(result) => result,
Err(payload) => panic_message(&payload),
};
if final_check_result.is_empty() {
FINAL_CHECK_RESULT.with(|r| *r.borrow_mut() = None);
return core::ptr::null();
}
FINAL_CHECK_RESULT.with(|r| {
let cstring = CString::new(final_check_result).unwrap_or_default();
let ptr = cstring.as_ptr();
*r.borrow_mut() = Some(cstring);
ptr
})
}
fn panic_message(payload: &(dyn core::any::Any + Send)) -> String {
if let Some(s) = payload.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"unknown error".to_string()
}
}