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        let mut registry = self.clone();
151        let lib_id = registry.fresh_lib_id();
152        LoadTransaction {
153            lib_id,
154            manifest,
155            trusted,
156            registry,
157            pending: PendingExports::default(),
158        }
159    }
160
161    /// Commits a [`LoadTransaction`], folding its staged registrations into this
162    /// registry and returning the new library id.
163    pub fn commit_load(&mut self, txn: LoadTransaction) -> Result<LibId> {
164        let lib_id = txn.lib_id;
165        let mut registry = txn.registry;
166        let sequence_before = self.catalog_sequence_snapshot();
167        commit_loaded_lib(
168            txn.lib_id,
169            &mut registry,
170            txn.manifest,
171            txn.trusted,
172            txn.pending,
173            sequence_before,
174        )?;
175        *self = registry;
176        Ok(lib_id)
177    }
178
179    pub(crate) fn ensure_export_available(&self, export: &Export) -> Result<()> {
180        let duplicate = self
181            .export_symbols
182            .get(&export.kind_symbol())
183            .is_some_and(|entries| entries.contains_key(export.symbol()));
184        if duplicate {
185            Err(Error::DuplicateExport {
186                kind: export.kind(),
187                symbol: export.symbol().clone(),
188            })
189        } else {
190            Ok(())
191        }
192    }
193
194    pub(crate) fn validate_export_record_against_manifest(
195        manifest: &LibManifest,
196        record: &ExportRecord,
197    ) -> Result<()> {
198        let declared = manifest
199            .exports
200            .iter()
201            .any(|export| export.kind_symbol() == record.kind && export.symbol() == &record.symbol);
202        if declared {
203            Ok(())
204        } else {
205            Err(Error::UndeclaredExportRecord {
206                kind: record.kind.clone(),
207                symbol: record.symbol.clone(),
208            })
209        }
210    }
211}