cairo_lang_sierra_generator/
executables.rs

1use std::collections::HashMap;
2
3use cairo_lang_diagnostics::ToOption;
4use cairo_lang_filesystem::ids::CrateId;
5use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
6use cairo_lang_sierra::ids::FunctionId;
7use cairo_lang_sierra::program::Program;
8use cairo_lang_syntax::node::helpers::QueryAttrs;
9use cairo_lang_utils::LookupIntern;
10use smol_str::SmolStr;
11
12use crate::db::SierraGenGroup;
13
14/// Find all functions with executable attributes.
15///
16/// _Executable attributes_ are plugin-backed attributes that declare a function as executable.
17/// You can create an _executable attribute_ by implementing the `executable_attributes` function
18/// of `MacroPlugin` trait.
19/// This function finds all functions in the syntactic model marked with an _executable attribute_,
20/// and returns the attribute names, along the function id.
21/// Note, that a single function can be marked with more than one executable attribute.
22/// Only crates declared as _main_crate_ids_ are considered.
23pub fn find_executable_function_ids(
24    db: &dyn SierraGenGroup,
25    main_crate_ids: Vec<CrateId>,
26) -> HashMap<ConcreteFunctionWithBodyId, Vec<SmolStr>> {
27    let executable_attributes = db
28        .macro_plugins()
29        .iter()
30        .flat_map(|plugin| plugin.executable_attributes())
31        .map(SmolStr::new)
32        .collect::<Vec<_>>();
33    let mut executable_function_ids = HashMap::new();
34    if !executable_attributes.is_empty() {
35        for crate_id in main_crate_ids {
36            for module in db.crate_modules(crate_id).iter() {
37                if let Some(free_functions) = db.module_free_functions(*module).to_option() {
38                    for (free_func_id, body) in free_functions.iter() {
39                        let found_attrs = executable_attributes
40                            .clone()
41                            .iter()
42                            .filter(|attr| body.has_attr(db.upcast(), attr.as_str()))
43                            .cloned()
44                            .collect::<Vec<_>>();
45                        if found_attrs.is_empty() {
46                            // No executable attributes found.
47                            continue;
48                        }
49                        // Find function corresponding to the node by full path.
50                        let function_id = ConcreteFunctionWithBodyId::from_no_generics_free(
51                            db.upcast(),
52                            *free_func_id,
53                        );
54                        if let Some(function_id) = function_id {
55                            executable_function_ids.insert(function_id, found_attrs);
56                        }
57                    }
58                }
59            }
60        }
61    }
62    executable_function_ids
63}
64
65/// Extract Sierra function ids for executable functions based on syntactic function ids.
66///
67/// This functions accepts executable function ids, found with `find_executable_function_ids`,
68/// and finds their corresponding Sierra ids in a Sierra program.
69/// The returned function ids are grouped by the executable attribute name, and sorted by full path.
70pub fn collect_executables(
71    db: &dyn SierraGenGroup,
72    mut executable_function_ids: HashMap<ConcreteFunctionWithBodyId, Vec<SmolStr>>,
73    sierra_program: &Program,
74) -> HashMap<SmolStr, Vec<FunctionId>> {
75    if executable_function_ids.is_empty() {
76        Default::default()
77    } else {
78        let executable_function_ids = executable_function_ids
79            .drain()
80            .filter_map(|(function_id, attrs)| {
81                let function_id = function_id
82                    .function_id(db.upcast())
83                    .to_option()
84                    .map(|function_id| db.intern_sierra_function(function_id));
85                function_id.map(|function_id| (function_id, attrs))
86            })
87            .collect::<HashMap<FunctionId, Vec<SmolStr>>>();
88        let mut result: HashMap<SmolStr, Vec<(String, FunctionId)>> = Default::default();
89        for function in &sierra_program.funcs {
90            let Some(found_attrs) = executable_function_ids.get(&function.id) else {
91                continue;
92            };
93            let full_path = function.id.lookup_intern(db).semantic_full_path(db.upcast());
94            for attr in found_attrs {
95                result
96                    .entry(attr.clone())
97                    .or_default()
98                    .push((full_path.clone(), function.id.clone()));
99            }
100        }
101        // Sort by full path for stability.
102        result
103            .drain()
104            .map(|(key, functions)| {
105                let mut functions = functions.into_iter().collect::<Vec<_>>();
106                functions.sort_by_key(|(full_path, _)| full_path.clone());
107                (key, functions.into_iter().map(|(_, function_id)| function_id).collect::<Vec<_>>())
108            })
109            .collect::<HashMap<SmolStr, Vec<FunctionId>>>()
110    }
111}