backhopper_xref/
builder.rs1use std::collections::{BTreeMap, BTreeSet};
8use std::mem;
9use std::path::PathBuf;
10
11use backhopper_core::{ApplicationName, Mfa, ModuleName};
12use backhopper_xref_graph::{
13 Building, CallGraph, CallTarget, FunctionRef, Functions, LocalFunctionRef, ModuleSummary,
14 VertexSet,
15};
16use backhopper_xref_reader::{ModuleData, ProjectLayout, ReadOutput, SourceReader};
17
18use crate::errors::XrefError;
19use crate::xref::Xref;
20
21#[derive(Debug, Clone, Default)]
22pub struct XrefBuilder {
23 layout: ProjectLayout,
24 modules: BTreeMap<ModuleName, ModuleData>,
25 applications: BTreeSet<ApplicationName>,
26 builtins: VertexSet,
27 warnings: Vec<backhopper_xref_reader::ReadWarning>,
28}
29
30impl XrefBuilder {
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 #[must_use]
36 pub fn with_layout(mut self, layout: ProjectLayout) -> Self {
37 self.layout = layout;
38 self
39 }
40
41 #[must_use]
42 pub fn with_builtins(mut self, b: VertexSet) -> Self {
43 self.builtins = b;
44 self
45 }
46
47 pub fn add_application<I>(
49 &mut self,
50 app: ApplicationName,
51 files: I,
52 ) -> Result<&mut Self, XrefError>
53 where
54 I: IntoIterator<Item = (PathBuf, Vec<u8>)>,
55 {
56 if !self.applications.insert(app.clone()) {
57 return Err(XrefError::DuplicateApplication(app));
58 }
59 let reader = SourceReader::with_layout(self.layout.clone());
60 let ReadOutput {
61 modules,
62 warnings,
63 paths: _,
64 } = reader.read_tree(files)?;
65 self.warnings.extend(warnings);
66 for m in modules {
67 let name = m.module.clone();
68 let new_app = m.application.assigned().map(|a| a.as_str().to_owned());
69 if let Some(prev) = self.modules.insert(name.clone(), m) {
70 let prev_app = prev.application.assigned().map(|a| a.as_str().to_owned());
71 self.warnings.push(
72 backhopper_xref_reader::ReadWarning::DuplicateModuleAcrossApplications {
73 name,
74 first_application: prev_app,
75 second_application: new_app,
76 },
77 );
78 }
79 }
80 Ok(self)
81 }
82
83 pub fn add_modules<I>(&mut self, modules: I) -> &mut Self
84 where
85 I: IntoIterator<Item = ModuleData>,
86 {
87 for m in modules {
88 self.modules.insert(m.module.clone(), m);
89 }
90 self
91 }
92
93 pub fn warnings(&self) -> &[backhopper_xref_reader::ReadWarning] {
94 &self.warnings
95 }
96
97 pub fn build(self) -> Result<Xref<Functions>, XrefError> {
98 let mut graph: CallGraph<Functions, Building> = CallGraph::new();
99 graph.set_builtins(self.builtins);
100 let mut modules = self.modules;
101 for (name, data) in &modules {
102 let summary = ModuleSummary {
103 application: data.application.assigned().cloned(),
104 exports: data.exports.clone(),
105 locals: data.locals.clone(),
106 on_load: data.on_load.clone(),
107 behaviours: data.behaviours.clone(),
108 callbacks_required: data.callbacks.clone(),
109 callbacks_optional: data.optional_callbacks.clone(),
110 };
111 graph.insert_module(name.clone(), summary);
112 }
113 for (_, data) in mem::take(&mut modules) {
114 let module = data.module;
115 for (sig, loc) in data.definitions {
116 let mfa = Mfa::new(module.clone(), sig.name, sig.arity);
117 graph.record_definition(mfa, loc);
118 }
119 if let Some(sig) = data.on_load {
120 graph.record_on_load(Mfa::new(module.clone(), sig.name, sig.arity));
121 }
122 for (sig, dep) in data.deprecated {
123 graph.record_deprecation(Mfa::new(module.clone(), sig.name, sig.arity), dep);
124 }
125 for cs in data.local_calls {
126 if let CallTarget::Local(LocalFunctionRef::Concrete { function, arity }) = cs.callee
127 {
128 let caller = Mfa::new(module.clone(), cs.caller.name, cs.caller.arity);
129 let callee = Mfa::new(module.clone(), function, arity);
130 graph.insert_local_call(caller, callee);
131 }
132 }
133 for cs in data.external_calls {
134 if let CallTarget::External(FunctionRef::Concrete(callee)) = cs.callee {
135 let caller = Mfa::new(module.clone(), cs.caller.name, cs.caller.arity);
136 graph.insert_external_call(caller, callee);
137 }
138 }
139 for u in data.unresolved {
140 graph.insert_unresolved(u);
141 }
142 }
143 Ok(Xref::from_graph(graph.finish(), self.warnings))
144 }
145}