Skip to main content

sim_kernel/library/registry/
load.rs

1use std::cmp::Ordering;
2use std::collections::{BTreeMap, BTreeSet};
3
4use crate::{
5    error::{Error, Result},
6    id::LibId,
7    library::{Export, ExportRecord, LibManifest, LoadTransaction, Version},
8};
9
10use super::Registry;
11use super::commit::commit_loaded_lib;
12use crate::library::loaders::compare_version_text;
13use crate::library::transaction::PendingExports;
14
15impl Registry {
16    /// Topologically orders the given manifests so each library's dependencies
17    /// load first.
18    ///
19    /// Already-loaded libraries satisfy dependencies. Errors with
20    /// [`DependencyVersionMismatch`](crate::error::Error::DependencyVersionMismatch),
21    /// [`MissingDependency`](crate::error::Error::MissingDependency), or
22    /// [`CyclicDependency`](crate::error::Error::CyclicDependency) when no order
23    /// exists.
24    pub fn dependency_order(&self, manifests: &[LibManifest]) -> Result<Vec<LibManifest>> {
25        let mut remaining = manifests.to_vec();
26        remaining.sort_by(|left, right| left.id.cmp(&right.id));
27        let mut loaded = self.libs_by_symbol.keys().cloned().collect::<BTreeSet<_>>();
28        let mut loaded_versions = self
29            .libs
30            .iter()
31            .map(|loaded| (loaded.manifest.id.clone(), loaded.manifest.version.clone()))
32            .collect::<BTreeMap<_, _>>();
33        let mut ordered = Vec::with_capacity(remaining.len());
34
35        while !remaining.is_empty() {
36            let mut progressed = false;
37            let mut index = 0;
38            while index < remaining.len() {
39                let ready = remaining[index].requires.iter().all(|dependency| {
40                    if !loaded.contains(&dependency.id) {
41                        return false;
42                    }
43                    match (
44                        loaded_versions.get(&dependency.id),
45                        dependency.minimum_version.as_ref(),
46                    ) {
47                        (Some(loaded_version), Some(required_version)) => {
48                            compare_version_text(&loaded_version.0, &required_version.0)
49                                != Ordering::Less
50                        }
51                        _ => true,
52                    }
53                });
54                if ready {
55                    let manifest = remaining.remove(index);
56                    loaded.insert(manifest.id.clone());
57                    loaded_versions.insert(manifest.id.clone(), manifest.version.clone());
58                    ordered.push(manifest);
59                    progressed = true;
60                } else {
61                    index += 1;
62                }
63            }
64
65            if !progressed {
66                let blocked = &remaining[0];
67                if let Some(dependency) = blocked.requires.iter().find(|dependency| {
68                    loaded_versions
69                        .get(&dependency.id)
70                        .zip(dependency.minimum_version.as_ref())
71                        .is_some_and(|(loaded_version, minimum)| {
72                            compare_version_text(&loaded_version.0, &minimum.0) == Ordering::Less
73                        })
74                }) {
75                    return Err(Error::DependencyVersionMismatch {
76                        lib: blocked.id.clone(),
77                        dependency: dependency.id.clone(),
78                        required: dependency
79                            .minimum_version
80                            .clone()
81                            .unwrap_or_else(|| Version(String::from("0"))),
82                        loaded: loaded_versions
83                            .get(&dependency.id)
84                            .cloned()
85                            .unwrap_or_else(|| Version(String::from("0"))),
86                    });
87                }
88                let missing = blocked
89                    .requires
90                    .iter()
91                    .find(|dependency| !loaded.contains(&dependency.id))
92                    .map(|dependency| dependency.id.clone())
93                    .unwrap_or_else(|| blocked.id.clone());
94                return Err(if missing == blocked.id {
95                    Error::CyclicDependency {
96                        symbol: blocked.id.clone(),
97                    }
98                } else {
99                    Error::MissingDependency {
100                        lib: blocked.id.clone(),
101                        dependency: missing,
102                    }
103                });
104            }
105        }
106
107        Ok(ordered)
108    }
109
110    /// Starts a [`LoadTransaction`] for a library on a private registry copy,
111    /// reserving its stable id.
112    ///
113    /// Nothing reaches `self` until the transaction is handed to
114    /// [`commit_load`](Registry::commit_load).
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use std::sync::Arc;
120    /// use sim_kernel::library::{
121    ///     AbiVersion, Export, LibManifest, LibTarget, Registry, Version,
122    /// };
123    /// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy, Symbol};
124    ///
125    /// let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
126    /// let answer = cx.factory().bool(true).unwrap();
127    ///
128    /// let manifest = LibManifest {
129    ///     id: Symbol::new("demo"),
130    ///     version: Version("0.1.0".to_owned()),
131    ///     abi: AbiVersion { major: 0, minor: 1 },
132    ///     target: LibTarget::HostRegistered,
133    ///     requires: Vec::new(),
134    ///     capabilities: Vec::new(),
135    ///     exports: vec![Export::Value { symbol: Symbol::new("answer") }],
136    /// };
137    ///
138    /// let mut registry = Registry::default();
139    /// let mut txn = registry.begin_load(manifest, true);
140    /// txn.linker().value(Symbol::new("answer"), answer.clone()).unwrap();
141    /// // Staged on the transaction; the live registry is still empty.
142    /// assert!(registry.value_by_symbol(&Symbol::new("answer")).is_none());
143    ///
144    /// let id = registry.commit_load(txn).unwrap();
145    /// assert!(registry.lib(&Symbol::new("demo")).is_some());
146    /// assert_eq!(registry.value_by_symbol(&Symbol::new("answer")), Some(&answer));
147    /// let _ = id;
148    /// ```
149    pub fn begin_load(&self, manifest: LibManifest, trusted: bool) -> LoadTransaction {
150        self.try_begin_load(manifest, trusted)
151            .unwrap_or_else(|err| panic!("{err}"))
152    }
153
154    /// Starts a load transaction, reporting catalog sequence allocation errors.
155    pub fn try_begin_load(&self, manifest: LibManifest, trusted: bool) -> Result<LoadTransaction> {
156        let mut registry = self.clone();
157        let lib_id = registry.try_fresh_lib_id()?;
158        Ok(LoadTransaction {
159            lib_id,
160            manifest,
161            trusted,
162            registry,
163            pending: PendingExports::default(),
164        })
165    }
166
167    /// Commits a [`LoadTransaction`], folding its staged registrations into this
168    /// registry and returning the new library id.
169    pub fn commit_load(&mut self, txn: LoadTransaction) -> Result<LibId> {
170        let lib_id = txn.lib_id;
171        let mut registry = txn.registry;
172        let sequence_before = self.catalog_sequence_snapshot();
173        commit_loaded_lib(
174            txn.lib_id,
175            &mut registry,
176            txn.manifest,
177            txn.trusted,
178            txn.pending,
179            sequence_before,
180        )?;
181        *self = registry;
182        Ok(lib_id)
183    }
184
185    pub(crate) fn ensure_export_available(&self, export: &Export) -> Result<()> {
186        let duplicate = self
187            .export_symbols
188            .get(&export.kind_symbol())
189            .is_some_and(|entries| entries.contains_key(export.symbol()));
190        if duplicate {
191            Err(Error::DuplicateExport {
192                kind: export.kind(),
193                symbol: export.symbol().clone(),
194            })
195        } else {
196            Ok(())
197        }
198    }
199
200    pub(crate) fn validate_export_record_against_manifest(
201        manifest: &LibManifest,
202        record: &ExportRecord,
203    ) -> Result<()> {
204        let declared = manifest
205            .exports
206            .iter()
207            .any(|export| export.kind_symbol() == record.kind && export.symbol() == &record.symbol);
208        if declared {
209            Ok(())
210        } else {
211            Err(Error::UndeclaredExportRecord {
212                kind: record.kind.clone(),
213                symbol: record.symbol.clone(),
214            })
215        }
216    }
217}