Skip to main content

luaur_compile_cli/functions/
compile_file.rs

1use alloc::string::String;
2use core::ffi::{c_char, c_void, CStr};
3use std::io::Write;
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_options::ParseOptions;
9use luaur_ast::records::parser::Parser;
10use luaur_bytecode::records::bytecode_builder::BytecodeBuilder;
11use luaur_cli_lib::functions::read_file::read_file;
12use luaur_code_gen::enums::code_gen_flags::CodeGenFlags;
13use luaur_code_gen::enums::include_cfg_info::IncludeCfgInfo;
14use luaur_code_gen::enums::include_ir_prefix::IncludeIrPrefix;
15use luaur_code_gen::enums::include_reg_flow_info::IncludeRegFlowInfo;
16use luaur_code_gen::enums::include_use_info::IncludeUseInfo;
17use luaur_code_gen::enums::target::Target;
18use luaur_code_gen::records::assembly_options::AssemblyOptions;
19use luaur_code_gen::records::compilation_options::CompilationOptions;
20use luaur_common::functions::get_clock::get_clock;
21use luaur_compiler::functions::compile_or_throw_compiler::compile_or_throw_bytecode_builder_parse_result_ast_name_table_compile_options;
22use luaur_compiler::records::compile_error::CompileError;
23
24use crate::enums::compile_format::CompileFormat;
25use crate::functions::annotate_instruction::annotate_instruction;
26use crate::functions::copts::copts;
27use crate::functions::get_codegen_assembly::get_codegen_assembly;
28use crate::functions::record_delta_time::record_delta_time;
29use crate::functions::report_error_compile::report_error_c_char_luau_parse_error;
30use crate::functions::report_error_compile_alt_b::report_error_c_char_luau_compile_error;
31use crate::records::compile_stats::CompileStats;
32use crate::records::global_options::globalOptions;
33
34pub fn compile_file(
35    name: *const c_char,
36    format: CompileFormat,
37    assembly_target: Target,
38    stats: &mut CompileStats,
39    dump_constants: bool,
40) -> bool {
41    let mut currts = get_clock();
42    let name_cstr = unsafe { CStr::from_ptr(name) };
43    let name_str = name_cstr.to_string_lossy();
44    let name_with_nul = String::from_utf8_lossy(name_cstr.to_bytes_with_nul()).into_owned();
45
46    let Some(source) = read_file(&name_with_nul) else {
47        eprintln!("Error opening {}", name_str);
48        return false;
49    };
50
51    stats.read_time += record_delta_time(&mut currts);
52
53    let result = catch_unwind(AssertUnwindSafe(|| {
54        let mut bcb = BytecodeBuilder::new(None);
55
56        let mut options = AssemblyOptions {
57            target: assembly_target,
58            compilation_options: CompilationOptions::default(),
59            output_binary: format == CompileFormat::CodegenNull,
60            include_assembly: false,
61            include_ir: false,
62            include_outlined_code: false,
63            include_ir_types: false,
64            include_ir_prefix: IncludeIrPrefix::default(),
65            include_use_info: IncludeUseInfo::default(),
66            include_cfg_info: IncludeCfgInfo::default(),
67            include_reg_flow_info: IncludeRegFlowInfo::default(),
68            annotator: Some(annotate_instruction),
69            annotator_context: &mut bcb as *mut BytecodeBuilder as *mut c_void,
70        };
71        options.compilation_options.flags = CodeGenFlags::CodeGen_ColdFunctions as u32;
72
73        if !options.output_binary {
74            options.include_assembly = format != CompileFormat::CodegenIr;
75            options.include_ir = format != CompileFormat::CodegenAsm;
76            options.include_ir_types = format != CompileFormat::CodegenAsm;
77            options.include_outlined_code = format == CompileFormat::CodegenVerbose;
78        }
79
80        if format == CompileFormat::Text {
81            let mut flags = BytecodeBuilder::DUMP_CODE
82                | BytecodeBuilder::DUMP_SOURCE
83                | BytecodeBuilder::DUMP_LOCALS
84                | BytecodeBuilder::DUMP_REMARKS
85                | BytecodeBuilder::DUMP_TYPES;
86            if dump_constants {
87                flags |= BytecodeBuilder::DUMP_CONSTANTS;
88            }
89            bcb.set_dump_flags(flags);
90            bcb.set_dump_source(&source);
91        } else if format == CompileFormat::Remarks {
92            bcb.set_dump_flags(BytecodeBuilder::DUMP_SOURCE | BytecodeBuilder::DUMP_REMARKS);
93            bcb.set_dump_source(&source);
94        } else if format == CompileFormat::Codegen
95            || format == CompileFormat::CodegenAsm
96            || format == CompileFormat::CodegenIr
97            || format == CompileFormat::CodegenVerbose
98        {
99            bcb.set_dump_flags(
100                BytecodeBuilder::DUMP_CODE
101                    | BytecodeBuilder::DUMP_SOURCE
102                    | BytecodeBuilder::DUMP_LOCALS
103                    | BytecodeBuilder::DUMP_REMARKS,
104            );
105            bcb.set_dump_source(&source);
106        }
107
108        stats.misc_time += record_delta_time(&mut currts);
109
110        let mut allocator = Allocator::allocator();
111        let mut names = AstNameTable::new(&mut allocator);
112        let mut parse_options = ParseOptions::default();
113        parse_options.store_cst_data = unsafe { globalOptions.parseCst };
114
115        let parse_result = Parser::parse(
116            source.as_str(),
117            source.len(),
118            &mut names,
119            &mut allocator,
120            parse_options,
121        );
122
123        if !parse_result.errors.is_empty() {
124            for error in &parse_result.errors {
125                report_error_c_char_luau_parse_error(name, error);
126            }
127            return false;
128        }
129
130        stats.lines += parse_result.lines;
131        stats.parse_time += record_delta_time(&mut currts);
132
133        if unsafe { globalOptions.onlyParse } {
134            return true;
135        }
136
137        let compile_options = copts();
138        compile_or_throw_bytecode_builder_parse_result_ast_name_table_compile_options(
139            &mut bcb,
140            &parse_result,
141            &mut names,
142            &compile_options,
143        );
144
145        stats.bytecode += bcb.get_bytecode().len();
146        stats.bytecode_instruction_count = bcb.get_total_instruction_count();
147        stats.compile_time += record_delta_time(&mut currts);
148
149        match format {
150            CompileFormat::Text => {
151                print!("{}", bcb.dump_everything());
152            }
153            CompileFormat::Remarks => {
154                print!("{}", bcb.dump_source_remarks());
155            }
156            CompileFormat::Binary => {
157                let _ = std::io::stdout().write_all(bcb.get_bytecode().as_bytes());
158            }
159            CompileFormat::Codegen
160            | CompileFormat::CodegenAsm
161            | CompileFormat::CodegenIr
162            | CompileFormat::CodegenVerbose => {
163                print!(
164                    "{}",
165                    get_codegen_assembly(name, bcb.get_bytecode(), options, &mut stats.lower_stats)
166                );
167            }
168            CompileFormat::CodegenNull => {
169                let assembly =
170                    get_codegen_assembly(name, bcb.get_bytecode(), options, &mut stats.lower_stats);
171                stats.codegen += assembly.len();
172                stats.codegen_time += record_delta_time(&mut currts);
173            }
174            CompileFormat::Null => {}
175        }
176
177        true
178    }));
179
180    match result {
181        Ok(success) => success,
182        Err(payload) => {
183            if let Some(error) = payload.downcast_ref::<CompileError>() {
184                report_error_c_char_luau_compile_error(name, error);
185                false
186            } else {
187                resume_unwind(payload);
188            }
189        }
190    }
191}