1use crate::{AstSnapshots, CompilerOptions};
22
23pub use leo_ast::{Ast, Program};
24use leo_ast::{NetworkName, NodeBuilder, Stub};
25use leo_errors::{CompilerError, Handler, Result};
26use leo_passes::*;
27use leo_span::{Symbol, source_map::FileName, with_session_globals};
28
29use std::{
30 ffi::OsStr,
31 fs,
32 path::{Path, PathBuf},
33 rc::Rc,
34};
35
36use indexmap::{IndexMap, IndexSet};
37use walkdir::WalkDir;
38
39pub struct CompiledProgram {
41 pub name: String,
43 pub bytecode: String,
45 pub abi: leo_abi::Program,
47}
48
49pub struct Compiled {
51 pub primary: CompiledProgram,
53 pub imports: Vec<CompiledProgram>,
55}
56
57pub struct Compiler {
59 output_directory: PathBuf,
61 pub program_name: Option<String>,
63 compiler_options: CompilerOptions,
65 state: CompilerState,
67 import_stubs: IndexMap<Symbol, Stub>,
69 pub statements_before_dce: u32,
71 pub statements_after_dce: u32,
73}
74
75impl Compiler {
76 pub fn parse(&mut self, source: &str, filename: FileName, modules: &[(&str, FileName)]) -> Result<()> {
77 let source_file = with_session_globals(|s| s.source_map.new_source(source, filename.clone()));
79
80 let modules = modules
82 .iter()
83 .map(|(source, filename)| with_session_globals(|s| s.source_map.new_source(source, filename.clone())))
84 .collect::<Vec<_>>();
85
86 self.state.ast = leo_parser::parse_ast(
88 self.state.handler.clone(),
89 &self.state.node_builder,
90 &source_file,
91 &modules,
92 self.state.network,
93 )?;
94
95 let program_scope = self.state.ast.ast.program_scopes.values().next().unwrap();
98 if self.program_name.is_none() {
99 self.program_name = Some(program_scope.program_id.name.to_string());
100 } else if self.program_name != Some(program_scope.program_id.name.to_string()) {
101 return Err(CompilerError::program_name_should_match_file_name(
102 program_scope.program_id.name,
103 if self.state.is_test {
105 format!(
106 "`{}` (the test file name)",
107 filename.to_string().split("/").last().expect("Could not get file name")
108 )
109 } else {
110 format!("`{}` (specified in `program.json`)", self.program_name.as_ref().unwrap())
111 },
112 program_scope.program_id.name.span,
113 )
114 .into());
115 }
116
117 if self.compiler_options.initial_ast {
118 self.write_ast_to_json("initial.json")?;
119 self.write_ast("initial.ast")?;
120 }
121
122 Ok(())
123 }
124
125 pub fn parse_and_return_ast(
127 &mut self,
128 source: &str,
129 filename: FileName,
130 modules: &[(&str, FileName)],
131 ) -> Result<Program> {
132 self.parse(source, filename, modules)?;
134
135 Ok(self.state.ast.ast.clone())
136 }
137
138 #[allow(clippy::too_many_arguments)]
140 pub fn new(
141 expected_program_name: Option<String>,
142 is_test: bool,
143 handler: Handler,
144 node_builder: Rc<NodeBuilder>,
145 output_directory: PathBuf,
146 compiler_options: Option<CompilerOptions>,
147 import_stubs: IndexMap<Symbol, Stub>,
148 network: NetworkName,
149 ) -> Self {
150 Self {
151 state: CompilerState {
152 handler,
153 node_builder: Rc::clone(&node_builder),
154 is_test,
155 network,
156 ..Default::default()
157 },
158 output_directory,
159 program_name: expected_program_name,
160 compiler_options: compiler_options.unwrap_or_default(),
161 import_stubs,
162 statements_before_dce: 0,
163 statements_after_dce: 0,
164 }
165 }
166
167 fn do_pass<P: Pass>(&mut self, input: P::Input) -> Result<P::Output> {
168 let output = P::do_pass(input, &mut self.state)?;
169
170 let write = match &self.compiler_options.ast_snapshots {
171 AstSnapshots::All => true,
172 AstSnapshots::Some(passes) => passes.contains(P::NAME),
173 };
174
175 if write {
176 self.write_ast_to_json(&format!("{}.json", P::NAME))?;
177 self.write_ast(&format!("{}.ast", P::NAME))?;
178 }
179
180 Ok(output)
181 }
182
183 pub fn intermediate_passes(&mut self) -> Result<(leo_abi::Program, IndexMap<String, leo_abi::Program>)> {
189 let type_checking_config = TypeCheckingInput::new(self.state.network);
190
191 self.do_pass::<NameValidation>(())?;
192
193 self.do_pass::<GlobalVarsCollection>(())?;
194
195 self.do_pass::<PathResolution>(())?;
196
197 self.do_pass::<GlobalItemsCollection>(())?;
198
199 self.do_pass::<TypeChecking>(type_checking_config.clone())?;
200
201 self.do_pass::<Disambiguate>(())?;
202
203 self.do_pass::<ProcessingAsync>(type_checking_config.clone())?;
204
205 self.do_pass::<StaticAnalyzing>(())?;
206
207 self.do_pass::<ConstPropUnrollAndMorphing>(type_checking_config.clone())?;
208
209 let abis = self.generate_abi();
212
213 self.do_pass::<StorageLowering>(type_checking_config.clone())?;
214
215 self.do_pass::<OptionLowering>(type_checking_config)?;
216
217 self.do_pass::<ProcessingScript>(())?;
218
219 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: true })?;
220
221 self.do_pass::<Destructuring>(())?;
222
223 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
224
225 self.do_pass::<WriteTransforming>(())?;
226
227 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
228
229 self.do_pass::<Flattening>(())?;
230
231 self.do_pass::<FunctionInlining>(())?;
232
233 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
235
236 self.do_pass::<SsaConstPropagation>(())?;
237
238 self.do_pass::<SsaForming>(SsaFormingInput { rename_defs: false })?;
239
240 self.do_pass::<CommonSubexpressionEliminating>(())?;
241
242 let output = self.do_pass::<DeadCodeEliminating>(())?;
243 self.statements_before_dce = output.statements_before;
244 self.statements_after_dce = output.statements_after;
245
246 Ok(abis)
247 }
248
249 fn generate_abi(&self) -> (leo_abi::Program, IndexMap<String, leo_abi::Program>) {
254 let primary_abi = leo_abi::generate(&self.state.ast.ast);
256
257 let import_abis: IndexMap<String, leo_abi::Program> = self
259 .state
260 .ast
261 .ast
262 .stubs
263 .iter()
264 .map(|(name, stub)| {
265 let abi = match stub {
266 Stub::FromLeo { program, .. } => leo_abi::generate(program),
267 Stub::FromAleo { program, .. } => leo_abi::aleo::generate(program),
268 };
269 (name.to_string(), abi)
270 })
271 .collect();
272
273 (primary_abi, import_abis)
274 }
275
276 pub fn compile(&mut self, source: &str, filename: FileName, modules: &Vec<(&str, FileName)>) -> Result<Compiled> {
291 self.parse(source, filename, modules)?;
293 self.add_import_stubs()?;
295 let (primary_abi, import_abis) = self.intermediate_passes()?;
297 let bytecodes = CodeGenerating::do_pass((), &mut self.state)?;
299
300 let primary = CompiledProgram {
302 name: self.program_name.clone().unwrap(),
303 bytecode: bytecodes.primary_bytecode,
304 abi: primary_abi,
305 };
306
307 let imports: Vec<CompiledProgram> = bytecodes
309 .import_bytecodes
310 .into_iter()
311 .map(|bc| {
312 let abi = import_abis.get(&bc.program_name).expect("ABI should exist for all imports").clone();
313 CompiledProgram { name: bc.program_name, bytecode: bc.bytecode, abi }
314 })
315 .collect();
316
317 Ok(Compiled { primary, imports })
318 }
319
320 fn read_sources_and_modules(
336 entry_file_path: impl AsRef<Path>,
337 source_directory: impl AsRef<Path>,
338 ) -> Result<(String, Vec<(String, FileName)>)> {
339 let source = fs::read_to_string(&entry_file_path)
341 .map_err(|e| CompilerError::file_read_error(entry_file_path.as_ref().display().to_string(), e))?;
342
343 let files = WalkDir::new(source_directory)
345 .into_iter()
346 .filter_map(Result::ok)
347 .filter(|e| {
348 e.file_type().is_file()
349 && e.path() != entry_file_path.as_ref()
350 && e.path().extension() == Some(OsStr::new("leo"))
351 })
352 .collect::<Vec<_>>();
353
354 let mut modules = Vec::new();
356 for file in &files {
357 let module_source = fs::read_to_string(file.path())
358 .map_err(|e| CompilerError::file_read_error(file.path().display().to_string(), e))?;
359 modules.push((module_source, FileName::Real(file.path().into())));
360 }
361
362 Ok((source, modules))
363 }
364
365 pub fn compile_from_directory(
367 &mut self,
368 entry_file_path: impl AsRef<Path>,
369 source_directory: impl AsRef<Path>,
370 ) -> Result<Compiled> {
371 let (source, modules_owned) = Self::read_sources_and_modules(&entry_file_path, &source_directory)?;
372
373 let module_refs: Vec<(&str, FileName)> =
375 modules_owned.iter().map(|(src, fname)| (src.as_str(), fname.clone())).collect();
376
377 self.compile(&source, FileName::Real(entry_file_path.as_ref().into()), &module_refs)
379 }
380
381 pub fn parse_from_directory(
383 &mut self,
384 entry_file_path: impl AsRef<Path>,
385 source_directory: impl AsRef<Path>,
386 ) -> Result<Program> {
387 let (source, modules_owned) = Self::read_sources_and_modules(&entry_file_path, &source_directory)?;
388
389 let module_refs: Vec<(&str, FileName)> =
391 modules_owned.iter().map(|(src, fname)| (src.as_str(), fname.clone())).collect();
392
393 self.parse(&source, FileName::Real(entry_file_path.as_ref().into()), &module_refs)?;
395 Ok(self.state.ast.ast.clone())
396 }
397
398 fn write_ast_to_json(&self, file_suffix: &str) -> Result<()> {
400 if self.compiler_options.ast_spans_enabled {
402 self.state.ast.to_json_file(
403 self.output_directory.clone(),
404 &format!("{}.{file_suffix}", self.program_name.as_ref().unwrap()),
405 )?;
406 } else {
407 self.state.ast.to_json_file_without_keys(
408 self.output_directory.clone(),
409 &format!("{}.{file_suffix}", self.program_name.as_ref().unwrap()),
410 &["_span", "span"],
411 )?;
412 }
413 Ok(())
414 }
415
416 fn write_ast(&self, file_suffix: &str) -> Result<()> {
418 let filename = format!("{}.{file_suffix}", self.program_name.as_ref().unwrap());
419 let full_filename = self.output_directory.join(&filename);
420 let contents = self.state.ast.ast.to_string();
421 fs::write(&full_filename, contents).map_err(|e| CompilerError::failed_ast_file(full_filename.display(), e))?;
422 Ok(())
423 }
424
425 pub fn add_import_stubs(&mut self) -> Result<()> {
440 let mut explored = IndexSet::<Symbol>::new();
442
443 let mut to_explore: Vec<Symbol> = self.state.ast.ast.imports.keys().cloned().collect();
445
446 if let Some(main_program_name) = self.program_name.clone() {
448 let main_symbol = Symbol::intern(&main_program_name);
449 for import in self.state.ast.ast.imports.keys() {
450 if let Some(child_stub) = self.import_stubs.get_mut(import) {
451 child_stub.add_parent(main_symbol);
452 }
453 }
454 }
455
456 while let Some(import_symbol) = to_explore.pop() {
458 explored.insert(import_symbol);
460
461 let Some(stub) = self.import_stubs.get(&import_symbol) else {
463 return Err(CompilerError::imported_program_not_found(
464 self.program_name.as_ref().unwrap(),
465 import_symbol,
466 self.state.ast.ast.imports[&import_symbol],
467 )
468 .into());
469 };
470
471 for child_symbol in stub.imports().cloned().collect::<Vec<_>>() {
472 if let Some(child_stub) = self.import_stubs.get_mut(&child_symbol) {
474 child_stub.add_parent(import_symbol);
475 }
476
477 if explored.insert(child_symbol) {
479 to_explore.push(child_symbol);
480 }
481 }
482 }
483
484 self.state.ast.ast.stubs = self
487 .import_stubs
488 .iter()
489 .filter(|(symbol, _stub)| explored.contains(*symbol))
490 .map(|(symbol, stub)| (*symbol, stub.clone()))
491 .collect();
492 Ok(())
493 }
494}