aiken_project/
module.rs

1use crate::{Error, Warning};
2use aiken_lang::{
3    IdGenerator,
4    ast::{
5        DataType, DataTypeKey, Definition, Function, FunctionAccessKey, Located, ModuleKind,
6        Tracing, TypedDataType, TypedFunction, TypedModule, TypedValidator, UntypedModule,
7        Validator,
8    },
9    expr::TypedExpr,
10    line_numbers::LineNumbers,
11    parser::extra::{Comment, ModuleExtra, comments_before},
12    tipo::TypeInfo,
13};
14use indexmap::IndexMap;
15use miette::NamedSource;
16use petgraph::{Direction, Graph, algo, graph::NodeIndex};
17use std::{
18    collections::{BTreeSet, HashMap},
19    io,
20    ops::{Deref, DerefMut},
21    path::PathBuf,
22};
23
24#[derive(Debug)]
25pub struct ParsedModule {
26    pub path: PathBuf,
27    pub name: String,
28    pub code: String,
29    pub kind: ModuleKind,
30    pub package: String,
31    pub ast: UntypedModule,
32    pub extra: ModuleExtra,
33}
34
35impl ParsedModule {
36    pub fn deps_for_graph(&self, env_modules: &[String]) -> (String, Vec<String>) {
37        let name = self.name.clone();
38        let deps: Vec<_> = self.ast.dependencies(env_modules);
39        (name, deps)
40    }
41
42    #[allow(clippy::too_many_arguments)]
43    #[allow(clippy::result_large_err)]
44    pub fn infer(
45        self,
46        id_gen: &IdGenerator,
47        package: &str,
48        tracing: Tracing,
49        env: Option<&str>,
50        validate_module_name: bool,
51        module_sources: &mut HashMap<String, (String, LineNumbers)>,
52        module_types: &mut HashMap<String, TypeInfo>,
53        functions: &mut IndexMap<FunctionAccessKey, TypedFunction>,
54        constants: &mut IndexMap<FunctionAccessKey, TypedExpr>,
55        data_types: &mut IndexMap<DataTypeKey, TypedDataType>,
56    ) -> Result<(CheckedModule, Vec<Warning>), Error> {
57        let mut warnings = Vec::new();
58
59        let ast = self
60            .ast
61            .infer(
62                id_gen,
63                self.kind,
64                package,
65                module_types,
66                tracing,
67                &mut warnings,
68                env,
69            )
70            .map_err(|error| Error::Type {
71                path: self.path.clone(),
72                src: self.code.clone(),
73                named: NamedSource::new(self.path.display().to_string(), self.code.clone()),
74                error: error.into(),
75            })?;
76
77        let warnings = warnings
78            .into_iter()
79            .map(|w| Warning::from_type_warning(w, self.path.clone(), self.code.clone()))
80            .collect::<Vec<_>>();
81
82        // Unless we're compiling prelude documentation, prevent keywords in module name
83        if validate_module_name {
84            ast.validate_module_name()?;
85        }
86
87        // Register module sources for an easier access later.
88        module_sources.insert(
89            self.name.clone(),
90            (self.code.clone(), LineNumbers::new(&self.code)),
91        );
92
93        // Register the types from this module so they can be
94        // imported into other modules.
95        module_types.insert(self.name.clone(), ast.type_info.clone());
96
97        // Register function definitions & data-types for easier access later.
98        ast.register_definitions(functions, constants, data_types);
99
100        Ok((
101            CheckedModule {
102                ast,
103                kind: self.kind,
104                extra: self.extra,
105                name: self.name,
106                code: self.code,
107                package: self.package,
108                input_path: self.path,
109            },
110            warnings,
111        ))
112    }
113}
114
115pub struct ParsedModules(HashMap<String, ParsedModule>);
116
117impl ParsedModules {
118    pub fn new() -> Self {
119        Self(HashMap::new())
120    }
121
122    #[allow(clippy::result_large_err)]
123    pub fn sequence(&self, our_modules: &BTreeSet<String>) -> Result<Vec<String>, Error> {
124        let env_modules = self
125            .0
126            .values()
127            .filter_map(|m| match m.kind {
128                ModuleKind::Env => Some(m.name.clone()),
129                ModuleKind::Lib | ModuleKind::Validator | ModuleKind::Config => None,
130            })
131            .collect::<Vec<String>>();
132
133        let inputs = self
134            .0
135            .values()
136            .map(|m| m.deps_for_graph(&env_modules))
137            .collect::<Vec<(String, Vec<String>)>>();
138
139        let capacity = inputs.len();
140
141        let mut graph = Graph::<String, ()>::with_capacity(capacity, capacity * 5);
142
143        let mut indices = HashMap::with_capacity(capacity);
144
145        let mut our_indices = BTreeSet::new();
146
147        for (value, _) in &inputs {
148            let index = graph.add_node(value.to_string());
149            indices.insert(value.clone(), index);
150            if our_modules.contains(value) {
151                our_indices.insert(index);
152            }
153        }
154
155        for (value, deps) in inputs {
156            if let Some(from_index) = indices.get(&value) {
157                let deps = deps.into_iter().filter_map(|dep| indices.get(&dep));
158
159                for to_index in deps {
160                    graph.add_edge(*from_index, *to_index, ());
161                }
162            }
163        }
164
165        let mut messed_up_indices = false;
166
167        // Prune the dependency graph to only keep nodes that have a path to one of our (i.e. the
168        // current project) module. This effectively prunes dependencies that are unused from the
169        // graph to ensure that we only compile the modules we actually depend on.
170        graph.retain_nodes(|graph, ix| {
171            // When discarding a node, indices in the graph end up being rewritten. Yet, we need to
172            // know starting indices for our search, so when we remove a dependency, we need find
173            // back what those indices are.
174            if messed_up_indices {
175                our_indices = BTreeSet::new();
176                for j in graph.node_indices() {
177                    if our_modules.contains(graph[j].as_str()) {
178                        our_indices.insert(j);
179                    }
180                }
181            }
182
183            for start in our_indices.iter() {
184                if algo::astar(&*graph, *start, |end| end == ix, |_| 1, |_| 0).is_some() {
185                    messed_up_indices = false;
186                    return true;
187                }
188            }
189
190            messed_up_indices = true;
191            false
192        });
193
194        match algo::toposort(&graph, None) {
195            Ok(sequence) => {
196                let sequence = sequence
197                    .iter()
198                    .filter_map(|i| graph.node_weight(*i))
199                    .rev()
200                    .cloned()
201                    .collect();
202
203                Ok(sequence)
204            }
205            Err(cycle) => {
206                let origin = cycle.node_id();
207
208                let mut path = vec![];
209
210                find_cycle(origin, origin, &graph, &mut path, &mut BTreeSet::new());
211
212                let modules = path
213                    .iter()
214                    .filter_map(|i| graph.node_weight(*i))
215                    .cloned()
216                    .collect();
217
218                Err(Error::ImportCycle { modules })
219            }
220        }
221    }
222}
223
224impl Default for ParsedModules {
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230impl From<HashMap<String, ParsedModule>> for ParsedModules {
231    fn from(parsed_modules: HashMap<String, ParsedModule>) -> Self {
232        ParsedModules(parsed_modules)
233    }
234}
235
236impl From<ParsedModules> for HashMap<String, ParsedModule> {
237    fn from(parsed_modules: ParsedModules) -> Self {
238        parsed_modules.0
239    }
240}
241
242impl Deref for ParsedModules {
243    type Target = HashMap<String, ParsedModule>;
244
245    fn deref(&self) -> &Self::Target {
246        &self.0
247    }
248}
249
250impl DerefMut for ParsedModules {
251    fn deref_mut(&mut self) -> &mut Self::Target {
252        &mut self.0
253    }
254}
255
256fn find_cycle<W>(
257    origin: NodeIndex,
258    parent: NodeIndex,
259    graph: &petgraph::Graph<W, ()>,
260    path: &mut Vec<NodeIndex>,
261    seen: &mut BTreeSet<NodeIndex>,
262) -> bool {
263    seen.insert(parent);
264
265    for node in graph.neighbors_directed(parent, Direction::Outgoing) {
266        if node == origin {
267            path.push(node);
268
269            return true;
270        }
271
272        if seen.contains(&node) {
273            continue;
274        }
275
276        if find_cycle(origin, node, graph, path, seen) {
277            path.push(node);
278
279            return true;
280        }
281    }
282
283    false
284}
285
286#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
287pub struct CheckedModule {
288    pub name: String,
289    pub code: String,
290    pub input_path: PathBuf,
291    pub kind: ModuleKind,
292    pub package: String,
293    pub ast: TypedModule,
294    pub extra: ModuleExtra,
295}
296
297impl CheckedModule {
298    pub fn skip_doc_generation(&self) -> bool {
299        self.ast
300            .docs
301            .first()
302            .map(|s| s.as_str().trim())
303            .unwrap_or_default()
304            == "@hidden"
305    }
306
307    pub fn to_cbor(&self) -> Vec<u8> {
308        let mut module_bytes = vec![];
309
310        ciborium::into_writer(&self, &mut module_bytes)
311            .expect("modules should not fail to serialize");
312
313        module_bytes
314    }
315
316    pub fn from_cbor(bytes: &[u8]) -> Result<Self, ciborium::de::Error<io::Error>> {
317        ciborium::from_reader(bytes)
318    }
319
320    pub fn to_cbor_hex(&self) -> (String, Vec<u8>) {
321        let module_bytes = self.to_cbor();
322        let hex_str = hex::encode(&module_bytes);
323
324        (hex_str, module_bytes)
325    }
326
327    pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
328        self.ast.find_node(byte_index)
329    }
330
331    pub fn attach_doc_and_module_comments(&mut self) {
332        // Module Comments
333        self.ast.docs = self
334            .extra
335            .module_comments
336            .iter()
337            .map(|span| {
338                Comment::from((span, self.code.as_str()))
339                    .content
340                    .to_string()
341            })
342            .collect();
343
344        // Order definitions to avoid dissociating doc comments from them
345        let mut definitions: Vec<_> = self.ast.definitions.iter_mut().collect();
346        definitions.sort_by(|a, b| a.location().start.cmp(&b.location().start));
347
348        // Doc Comments
349        let mut doc_comments = self.extra.doc_comments.iter().peekable();
350        for def in &mut definitions {
351            let docs: Vec<&str> =
352                comments_before(&mut doc_comments, def.location().start, &self.code);
353            if !docs.is_empty() {
354                let doc = docs.join("\n");
355                def.put_doc(doc);
356            }
357
358            match def {
359                Definition::DataType(DataType { constructors, .. }) => {
360                    for constructor in constructors {
361                        let docs: Vec<&str> = comments_before(
362                            &mut doc_comments,
363                            constructor.location.start,
364                            &self.code,
365                        );
366                        if !docs.is_empty() {
367                            let doc = docs.join("\n");
368                            constructor.put_doc(doc);
369                        }
370
371                        for argument in constructor.arguments.iter_mut() {
372                            let docs: Vec<&str> = comments_before(
373                                &mut doc_comments,
374                                argument.location.start,
375                                &self.code,
376                            );
377                            if !docs.is_empty() {
378                                let doc = docs.join("\n");
379                                argument.put_doc(doc);
380                            }
381                        }
382                    }
383                }
384                Definition::Fn(Function { arguments, .. }) => {
385                    for argument in arguments {
386                        let docs: Vec<&str> =
387                            comments_before(&mut doc_comments, argument.location.start, &self.code);
388
389                        if !docs.is_empty() {
390                            let doc = docs.join("\n");
391                            argument.put_doc(doc);
392                        }
393                    }
394                }
395                Definition::Validator(Validator {
396                    params,
397                    handlers,
398                    fallback,
399                    ..
400                }) => {
401                    for param in params {
402                        let docs: Vec<&str> =
403                            comments_before(&mut doc_comments, param.location.start, &self.code);
404
405                        if !docs.is_empty() {
406                            let doc = docs.join("\n");
407                            param.put_doc(doc);
408                        }
409                    }
410
411                    for handler in handlers.iter_mut() {
412                        for argument in handler.arguments.iter_mut() {
413                            let docs: Vec<&str> = comments_before(
414                                &mut doc_comments,
415                                argument.location.start,
416                                &self.code,
417                            );
418
419                            if !docs.is_empty() {
420                                let doc = docs.join("\n");
421                                argument.put_doc(doc);
422                            }
423                        }
424                    }
425
426                    for argument in fallback.arguments.iter_mut() {
427                        let docs: Vec<&str> =
428                            comments_before(&mut doc_comments, argument.location.start, &self.code);
429
430                        if !docs.is_empty() {
431                            let doc = docs.join("\n");
432                            argument.put_doc(doc);
433                        }
434                    }
435                }
436                _ => (),
437            }
438        }
439    }
440}
441
442#[derive(Default, Debug, Clone)]
443pub struct CheckedModules(HashMap<String, CheckedModule>);
444
445impl From<HashMap<String, CheckedModule>> for CheckedModules {
446    fn from(checked_modules: HashMap<String, CheckedModule>) -> Self {
447        CheckedModules(checked_modules)
448    }
449}
450
451impl From<CheckedModules> for HashMap<String, CheckedModule> {
452    fn from(checked_modules: CheckedModules) -> Self {
453        checked_modules.0
454    }
455}
456
457impl<'a> From<&'a CheckedModules> for &'a HashMap<String, CheckedModule> {
458    fn from(checked_modules: &'a CheckedModules) -> Self {
459        &checked_modules.0
460    }
461}
462
463impl CheckedModules {
464    pub fn singleton(module: CheckedModule) -> Self {
465        let mut modules = Self::default();
466        modules.insert(module.name.clone(), module);
467        modules
468    }
469
470    // todo: this might need fixing
471    pub fn validators(&self) -> impl Iterator<Item = (&CheckedModule, &TypedValidator)> {
472        let mut items = vec![];
473
474        for validator_module in self.0.values().filter(|module| module.kind.is_validator()) {
475            for some_definition in validator_module.ast.definitions() {
476                if let Definition::Validator(def) = some_definition {
477                    items.push((validator_module, def));
478                }
479            }
480        }
481
482        items.sort_by(|left, right| {
483            (
484                left.0.package.to_string(),
485                left.0.name.to_string(),
486                left.1.name.to_string(),
487            )
488                .cmp(&(
489                    right.0.package.to_string(),
490                    right.0.name.to_string(),
491                    right.1.name.to_string(),
492                ))
493        });
494
495        items.into_iter()
496    }
497
498    pub fn functions(&self) -> impl Iterator<Item = (&CheckedModule, &TypedFunction)> {
499        let mut items = vec![];
500
501        for module in self.0.values() {
502            for some_definition in module.ast.definitions() {
503                if let Definition::Fn(def) = some_definition {
504                    items.push((module, def));
505                }
506            }
507        }
508
509        items.into_iter()
510    }
511
512    pub fn into_validators(self) -> impl Iterator<Item = CheckedModule> {
513        self.0
514            .into_values()
515            .filter(|module| module.kind.is_validator())
516    }
517}
518
519impl Deref for CheckedModules {
520    type Target = HashMap<String, CheckedModule>;
521
522    fn deref(&self) -> &Self::Target {
523        &self.0
524    }
525}
526
527impl DerefMut for CheckedModules {
528    fn deref_mut(&mut self) -> &mut Self::Target {
529        &mut self.0
530    }
531}