Skip to main content

luaur_bytecode_cli/functions/
analyze_file.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use std::ffi::CString;
4use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
5
6use luaur_ast::records::allocator::Allocator;
7use luaur_ast::records::ast_name_table::AstNameTable;
8use luaur_ast::records::parse_error::ParseError;
9use luaur_ast::records::parse_options::ParseOptions;
10use luaur_ast::records::parser::Parser;
11use luaur_bytecode::records::bytecode_builder::BytecodeBuilder;
12use luaur_cli_lib::functions::read_file::read_file;
13use luaur_code_gen::functions::summarize_bytecode::summarize_bytecode;
14use luaur_code_gen::records::function_bytecode_summary::FunctionBytecodeSummary;
15use luaur_compiler::functions::compile_or_throw_compiler::compile_or_throw_bytecode_builder_parse_result_ast_name_table_compile_options;
16use luaur_compiler::records::compile_error::CompileError;
17use luaur_vm::functions::lua_close::lua_close;
18use luaur_vm::functions::lua_l_newstate::lua_l_newstate;
19use luaur_vm::functions::luau_load::luau_load;
20use luaur_vm::type_aliases::lua_state::lua_State;
21
22use crate::functions::copts::copts;
23
24struct LuaStateGuard(*mut lua_State);
25
26impl Drop for LuaStateGuard {
27    fn drop(&mut self) {
28        if !self.0.is_null() {
29            unsafe {
30                lua_close(self.0);
31            }
32        }
33    }
34}
35
36fn nul_terminated(value: &str) -> Option<String> {
37    CString::new(value)
38        .ok()
39        .and_then(|value| String::from_utf8(value.as_bytes_with_nul().to_vec()).ok())
40}
41
42fn report_parse_error(name: &str, error: &ParseError) {
43    let location = error.get_location();
44    eprintln!(
45        "{}({},{}): SyntaxError: {}",
46        name,
47        location.begin.line + 1,
48        location.begin.column + 1,
49        error.what()
50    );
51}
52
53fn report_compile_error(name: &str, error: &CompileError) {
54    let location = error.get_location();
55    eprintln!(
56        "{}({},{}): CompileError: {}",
57        name,
58        location.begin.line + 1,
59        location.begin.column + 1,
60        error
61    );
62}
63
64pub fn analyze_file(
65    name: &str,
66    nesting_limit: u32,
67    summaries: &mut Vec<FunctionBytecodeSummary>,
68) -> bool {
69    let Some(name_with_nul) = nul_terminated(name) else {
70        eprintln!("Error opening {}", name);
71        return false;
72    };
73
74    let Some(source) = read_file(&name_with_nul) else {
75        eprintln!("Error opening {}", name);
76        return false;
77    };
78
79    let mut allocator = Allocator::allocator();
80    let mut names = AstNameTable::new(&mut allocator);
81    let parse_options = ParseOptions::default();
82    let parse_result = Parser::parse(
83        source.as_str(),
84        source.len(),
85        &mut names,
86        &mut allocator,
87        parse_options,
88    );
89
90    if !parse_result.errors.is_empty() {
91        for error in &parse_result.errors {
92            report_parse_error(name, error);
93        }
94
95        return false;
96    }
97
98    let mut bcb = BytecodeBuilder::new(None);
99    let options = copts();
100    let compile_result = catch_unwind(AssertUnwindSafe(|| {
101        compile_or_throw_bytecode_builder_parse_result_ast_name_table_compile_options(
102            &mut bcb,
103            &parse_result,
104            &mut names,
105            &options,
106        );
107    }));
108
109    if let Err(payload) = compile_result {
110        if let Some(error) = payload.downcast_ref::<CompileError>() {
111            report_compile_error(name, error);
112            return false;
113        }
114
115        resume_unwind(payload);
116    }
117
118    let bytecode = bcb.get_bytecode();
119    let global_state = LuaStateGuard(lua_l_newstate());
120    let l = global_state.0;
121
122    if unsafe {
123        luau_load(
124            l,
125            name_with_nul.as_ptr() as *const core::ffi::c_char,
126            bytecode.as_ptr() as *const core::ffi::c_char,
127            bytecode.len(),
128            0,
129        )
130    } == 0
131    {
132        *summaries = summarize_bytecode(l, -1, nesting_limit);
133        true
134    } else {
135        eprintln!("Error loading bytecode {}", name);
136        false
137    }
138}