1use std::{collections::HashMap, fmt::Display};
4
5use petgraph::stable_graph::{NodeIndex, StableDiGraph};
6use stable_vec::StableVec;
7
8use crate::{
9 compartment::{Compartment, CompartmentId},
10 engines::ContextEngine,
11 library::{Library, LibraryId, UnloadedLibrary},
12 DynlinkError, DynlinkErrorKind,
13};
14
15mod deps;
16mod load;
17pub(crate) mod relocate;
18pub mod runtime;
19mod syms;
20
21pub use load::LoadIds;
22
23#[repr(C)]
24pub struct Context {
26 pub(crate) engine: Box<dyn ContextEngine + Send>,
28 compartment_names: HashMap<String, usize>,
30 compartments: StableVec<Compartment>,
32
33 pub(crate) library_deps: StableDiGraph<LoadedOrUnloaded, ()>,
37}
38
39pub enum LoadedOrUnloaded {
43 Unloaded(UnloadedLibrary),
44 Loaded(Library),
45}
46
47impl Display for LoadedOrUnloaded {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 LoadedOrUnloaded::Unloaded(unlib) => write!(f, "(unloaded){}", unlib),
51 LoadedOrUnloaded::Loaded(lib) => write!(f, "(loaded){}", lib),
52 }
53 }
54}
55
56impl LoadedOrUnloaded {
57 pub fn name(&self) -> &str {
59 match self {
60 LoadedOrUnloaded::Unloaded(unlib) => &unlib.name,
61 LoadedOrUnloaded::Loaded(lib) => &lib.name,
62 }
63 }
64
65 pub fn loaded(&self) -> Option<&Library> {
67 match self {
68 LoadedOrUnloaded::Unloaded(_) => None,
69 LoadedOrUnloaded::Loaded(lib) => Some(lib),
70 }
71 }
72
73 pub fn loaded_mut(&mut self) -> Option<&mut Library> {
75 match self {
76 LoadedOrUnloaded::Unloaded(_) => None,
77 LoadedOrUnloaded::Loaded(lib) => Some(lib),
78 }
79 }
80}
81
82impl Context {
83 pub fn new(engine: Box<dyn ContextEngine + Send>) -> Self {
85 Self {
86 engine,
87 compartment_names: HashMap::new(),
88 library_deps: StableDiGraph::new(),
89 compartments: StableVec::new(),
90 }
91 }
92
93 pub fn replace_engine(&mut self, engine: Box<dyn ContextEngine + Send>) {
95 self.engine = engine;
96 }
97
98 pub fn lookup_compartment(&self, name: &str) -> Option<CompartmentId> {
100 Some(CompartmentId(*self.compartment_names.get(name)?))
101 }
102
103 pub fn get_compartment(&self, id: CompartmentId) -> Result<&Compartment, DynlinkError> {
105 if !self.compartments.has_element_at(id.0) {
106 return Err(DynlinkErrorKind::InvalidCompartmentId { id }.into());
107 }
108 Ok(&self.compartments[id.0])
109 }
110
111 pub fn get_compartment_mut(
113 &mut self,
114 id: CompartmentId,
115 ) -> Result<&mut Compartment, DynlinkError> {
116 if !self.compartments.has_element_at(id.0) {
117 return Err(DynlinkErrorKind::InvalidCompartmentId { id }.into());
118 }
119 Ok(&mut self.compartments[id.0])
120 }
121
122 pub fn lookup_library(&self, comp: CompartmentId, name: &str) -> Option<LibraryId> {
124 let comp = self.get_compartment(comp).ok()?;
125 Some(LibraryId(*comp.library_names.get(name)?))
126 }
127
128 pub fn get_library(&self, id: LibraryId) -> Result<&Library, DynlinkError> {
130 if !self.library_deps.contains_node(id.0) {
131 return Err(DynlinkErrorKind::InvalidLibraryId { id }.into());
132 }
133 match &self.library_deps[id.0] {
134 LoadedOrUnloaded::Unloaded(unlib) => Err(DynlinkErrorKind::UnloadedLibrary {
135 library: unlib.name.to_string(),
136 }
137 .into()),
138 LoadedOrUnloaded::Loaded(lib) => Ok(lib),
139 }
140 }
141
142 pub fn get_library_mut(&mut self, id: LibraryId) -> Result<&mut Library, DynlinkError> {
144 if !self.library_deps.contains_node(id.0) {
145 return Err(DynlinkErrorKind::InvalidLibraryId { id }.into());
146 }
147 match &mut self.library_deps[id.0] {
148 LoadedOrUnloaded::Unloaded(unlib) => Err(DynlinkErrorKind::UnloadedLibrary {
149 library: unlib.name.to_string(),
150 }
151 .into()),
152 LoadedOrUnloaded::Loaded(lib) => Ok(lib),
153 }
154 }
155
156 pub fn with_dfs_postorder<R>(
158 &self,
159 root_id: LibraryId,
160 mut f: impl FnMut(&LoadedOrUnloaded) -> R,
161 ) -> Vec<R> {
162 let mut rets = vec![];
163 let mut visit = petgraph::visit::DfsPostOrder::new(&self.library_deps, root_id.0);
164 while let Some(node) = visit.next(&self.library_deps) {
165 let dep = &self.library_deps[node];
166 rets.push(f(dep))
167 }
168 rets
169 }
170
171 pub fn with_dfs_postorder_mut<R>(
174 &mut self,
175 root_id: LibraryId,
176 mut f: impl FnMut(&mut LoadedOrUnloaded) -> R,
177 ) -> Vec<R> {
178 let mut rets = vec![];
179 let mut visit = petgraph::visit::DfsPostOrder::new(&self.library_deps, root_id.0);
180 while let Some(node) = visit.next(&self.library_deps) {
181 let dep = &mut self.library_deps[node];
182 rets.push(f(dep))
183 }
184 rets
185 }
186
187 pub fn with_bfs(&self, root_id: LibraryId, mut f: impl FnMut(&LoadedOrUnloaded) -> bool) {
189 let mut visit = petgraph::visit::Bfs::new(&self.library_deps, root_id.0);
190 while let Some(node) = visit.next(&self.library_deps) {
191 let dep = &self.library_deps[node];
192 if !f(dep) {
193 return;
194 }
195 }
196 }
197
198 pub fn libraries(&self) -> LibraryIter<'_> {
199 LibraryIter { ctx: self, next: 0 }
200 }
201
202 pub(crate) fn add_library(&mut self, lib: UnloadedLibrary) -> NodeIndex {
203 self.library_deps.add_node(LoadedOrUnloaded::Unloaded(lib))
204 }
205
206 pub(crate) fn add_dep(&mut self, parent: NodeIndex, dep: NodeIndex) {
207 self.library_deps.add_edge(parent, dep, ());
208 }
209
210 pub fn add_manual_dependency(&mut self, parent: LibraryId, dependee: LibraryId) {
211 self.add_dep(parent.0, dependee.0);
212 }
213
214 pub fn unload_compartment(
215 &mut self,
216 comp_id: CompartmentId,
217 ) -> (Option<Compartment>, Vec<LoadedOrUnloaded>) {
218 let Ok(comp) = self.get_compartment(comp_id) else {
219 return (None, vec![]);
220 };
221 let name = comp.name.clone();
222 let ids = comp.library_ids();
223 let nodes = ids
224 .collect::<Vec<_>>()
225 .iter()
226 .filter_map(|id| self.library_deps.remove_node(id.0))
227 .collect();
228 self.compartment_names.remove(&name);
229 (self.compartments.remove(comp_id.0), nodes)
230 }
231
232 pub fn add_compartment(
234 &mut self,
235 name: impl ToString,
236 new_comp_flags: NewCompartmentFlags,
237 ) -> Result<CompartmentId, DynlinkError> {
238 let name = name.to_string();
239 let idx = self.compartments.next_push_index();
240 let comp = Compartment::new(name.clone(), CompartmentId(idx), new_comp_flags);
241 self.compartments.push(comp);
242 tracing::debug!("added compartment {} with ID {}", name, idx);
243 self.compartment_names.insert(name, idx);
244 Ok(CompartmentId(idx))
245 }
246
247 pub fn compartment_dependencies(
249 &self,
250 id: CompartmentId,
251 ) -> Result<Vec<CompartmentId>, DynlinkError> {
252 let comp = self.get_compartment(id)?;
253 let mut deps = vec![];
254 for lib in comp.library_ids() {
255 for n in self.library_deps.neighbors(lib.0) {
256 let neigh = self.library_deps[n].loaded().unwrap();
257 deps.push(neigh.comp_id);
258 }
259 }
260 deps.sort_unstable();
261 deps.dedup();
262 if let Some(dep) = deps.iter().position(|dep| *dep == id) {
263 deps.remove(dep);
264 }
265 Ok(deps)
266 }
267}
268
269pub struct LibraryIter<'a> {
270 ctx: &'a Context,
271 next: usize,
272}
273
274impl<'a> Iterator for LibraryIter<'a> {
275 type Item = &'a Library;
276
277 fn next(&mut self) -> Option<Self::Item> {
278 loop {
279 let idx = self.ctx.library_deps.node_indices().nth(self.next)?;
280 self.next += 1;
281 let node = &self.ctx.library_deps[idx];
282 match node {
283 LoadedOrUnloaded::Unloaded(_) => {}
284 LoadedOrUnloaded::Loaded(lib) => return Some(lib),
285 }
286 }
287 }
288}
289
290bitflags::bitflags! {
291 #[derive(Clone, Copy, Debug)]
292 pub struct NewCompartmentFlags : u32 {
293 const EXPORT_GATES = 0x1;
294 const DEBUG = 0x2;
295 }
296}