cairo_lang_sierra_generator/
executables.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::collections::HashMap;

use cairo_lang_diagnostics::ToOption;
use cairo_lang_filesystem::ids::CrateId;
use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
use cairo_lang_sierra::ids::FunctionId;
use cairo_lang_sierra::program::Program;
use cairo_lang_syntax::node::helpers::QueryAttrs;
use cairo_lang_utils::LookupIntern;
use smol_str::SmolStr;

use crate::db::SierraGenGroup;

/// Find all functions with executable attributes.
///
/// _Executable attributes_ are plugin-backed attributes that declare a function as executable.
/// You can create an _executable attribute_ by implementing the `executable_attributes` function
/// of `MacroPlugin` trait.
/// This function finds all functions in the syntactic model marked with an _executable attribute_,
/// and returns the attribute names, along the function id.
/// Note, that a single function can be marked with more than one executable attribute.
/// Only crates declared as _main_crate_ids_ are considered.
pub fn find_executable_function_ids(
    db: &dyn SierraGenGroup,
    main_crate_ids: Vec<CrateId>,
) -> HashMap<ConcreteFunctionWithBodyId, Vec<SmolStr>> {
    let executable_attributes = db
        .macro_plugins()
        .iter()
        .flat_map(|plugin| plugin.executable_attributes())
        .map(SmolStr::new)
        .collect::<Vec<_>>();
    let mut executable_function_ids = HashMap::new();
    if !executable_attributes.is_empty() {
        for crate_id in main_crate_ids {
            for module in db.crate_modules(crate_id).iter() {
                if let Some(free_functions) = db.module_free_functions(*module).to_option() {
                    for (free_func_id, body) in free_functions.iter() {
                        let found_attrs = executable_attributes
                            .clone()
                            .iter()
                            .filter(|attr| body.has_attr(db.upcast(), attr.as_str()))
                            .cloned()
                            .collect::<Vec<_>>();
                        if found_attrs.is_empty() {
                            // No executable attributes found.
                            continue;
                        }
                        // Find function corresponding to the node by full path.
                        let function_id = ConcreteFunctionWithBodyId::from_no_generics_free(
                            db.upcast(),
                            *free_func_id,
                        );
                        if let Some(function_id) = function_id {
                            executable_function_ids.insert(function_id, found_attrs);
                        }
                    }
                }
            }
        }
    }
    executable_function_ids
}

/// Extract Sierra function ids for executable functions based on syntactic function ids.
///
/// This functions accepts executable function ids, found with `find_executable_function_ids`,
/// and finds their corresponding Sierra ids in a Sierra program.
/// The returned function ids are grouped by the executable attribute name, and sorted by full path.
pub fn collect_executables(
    db: &dyn SierraGenGroup,
    mut executable_function_ids: HashMap<ConcreteFunctionWithBodyId, Vec<SmolStr>>,
    sierra_program: &Program,
) -> HashMap<SmolStr, Vec<FunctionId>> {
    if executable_function_ids.is_empty() {
        Default::default()
    } else {
        let executable_function_ids = executable_function_ids
            .drain()
            .filter_map(|(function_id, attrs)| {
                let function_id = function_id
                    .function_id(db.upcast())
                    .to_option()
                    .map(|function_id| db.intern_sierra_function(function_id));
                function_id.map(|function_id| (function_id, attrs))
            })
            .collect::<HashMap<FunctionId, Vec<SmolStr>>>();
        let mut result: HashMap<SmolStr, Vec<(String, FunctionId)>> = Default::default();
        for function in &sierra_program.funcs {
            let Some(found_attrs) = executable_function_ids.get(&function.id) else {
                continue;
            };
            let full_path = function.id.lookup_intern(db).semantic_full_path(db.upcast());
            for attr in found_attrs {
                result
                    .entry(attr.clone())
                    .or_default()
                    .push((full_path.clone(), function.id.clone()));
            }
        }
        // Sort by full path for stability.
        result
            .drain()
            .map(|(key, functions)| {
                let mut functions = functions.into_iter().collect::<Vec<_>>();
                functions.sort_by_key(|(full_path, _)| full_path.clone());
                (key, functions.into_iter().map(|(_, function_id)| function_id).collect::<Vec<_>>())
            })
            .collect::<HashMap<SmolStr, Vec<FunctionId>>>()
    }
}