Skip to main content

sim_kernel/library/
cx.rs

1use crate::{
2    LibId,
3    env::Cx,
4    error::{Error, Result},
5    library::{Lib, LibTarget, Version},
6};
7
8use super::claims::publish_loaded_lib_claims;
9use super::loaders::dependency_satisfied;
10use std::collections::BTreeMap;
11
12impl Cx {
13    /// Atomically activates an already constructed library candidate.
14    ///
15    /// `None` admits only an absent symbol. `Some(id)` replaces exactly that
16    /// loaded leaf while retaining the library and matching export ids. Only
17    /// kernel-observable library state is staged; provider effects are outside
18    /// this transaction because `Lib::load` receives only `LoadCx` and `Linker`.
19    pub fn activate_lib(&mut self, expected: Option<LibId>, lib: &dyn Lib) -> Result<LibId> {
20        let manifest = lib.manifest();
21        let current = self.registry.lib(&manifest.id).cloned();
22        match (expected, current.as_ref()) {
23            (None, None) => {}
24            (None, Some(_)) => {
25                return Err(Error::DuplicateLib {
26                    symbol: manifest.id.clone(),
27                });
28            }
29            (Some(id), Some(loaded)) if loaded.id == id => {}
30            (Some(id), _) => {
31                return Err(Error::Lib(format!(
32                    "activation expected library id {id:?} for {}",
33                    manifest.id
34                )));
35            }
36        }
37
38        if expected.is_none() {
39            let mut staged = self.library_state();
40            let id = self.with_library_state(&mut staged, |cx| cx.load_lib(lib))?;
41            self.commit_library_state(staged);
42            return Ok(id);
43        }
44
45        let loaded = current.expect("validated loaded replacement");
46        let id = loaded.id;
47        let stable_exports = loaded
48            .exports
49            .iter()
50            .filter_map(|record| match record.state {
51                crate::library::ExportState::Resolved { id } => {
52                    Some(((record.kind.clone(), record.symbol.clone()), id))
53                }
54                _ => None,
55            })
56            .collect::<BTreeMap<_, _>>();
57        let mut staged = self.library_state();
58        let result = self.with_library_state(&mut staged, |cx| {
59            for capability in &manifest.capabilities {
60                cx.require(capability)?;
61            }
62            for dependency in &manifest.requires {
63                if let Some(found) = cx.registry.manifest_by_symbol(&dependency.id)
64                    && !dependency_satisfied(found, dependency)
65                {
66                    return Err(Error::DependencyVersionMismatch {
67                        lib: manifest.id.clone(),
68                        dependency: dependency.id.clone(),
69                        required: dependency
70                            .minimum_version
71                            .clone()
72                            .unwrap_or_else(|| Version(String::from("0"))),
73                        loaded: found.version.clone(),
74                    });
75                }
76            }
77            let sequence_floor = cx.registry.catalog_sequence_snapshot();
78            cx.registry.unload(id)?;
79            cx.remove_load_claims(&[id]);
80            cx.registry.retain_sequence_floor(&sequence_floor)?;
81            let trusted = matches!(manifest.target, LibTarget::HostRegistered);
82            let mut txn = cx.registry.try_begin_load(manifest.clone(), trusted)?;
83            txn.lib_id = id;
84            txn.stable_exports = stable_exports;
85            {
86                let mut load_cx = cx.load_cx();
87                lib.load(&mut load_cx, &mut txn.linker())?;
88            }
89            let committed = cx.registry.commit_load(txn)?;
90            let loaded = cx
91                .registry
92                .libs()
93                .iter()
94                .find(|item| item.id == committed)
95                .cloned()
96                .ok_or_else(|| Error::Lib("activated library missing after commit".to_owned()))?;
97            let claims = publish_loaded_lib_claims(cx, &loaded)?;
98            cx.record_load_claims(committed, claims);
99            Ok(committed)
100        })?;
101        self.commit_library_state(staged);
102        Ok(result)
103    }
104
105    /// Loads a single library: checks its requested capabilities and dependency
106    /// versions, runs its [`Lib::load`](crate::library::Lib::load) against a
107    /// load transaction, commits the result, and publishes its load claims.
108    pub fn load_lib(&mut self, lib: &dyn Lib) -> Result<LibId> {
109        let manifest = lib.manifest();
110        for capability in &manifest.capabilities {
111            self.require(capability)?;
112        }
113        for dependency in &manifest.requires {
114            if let Some(loaded_manifest) = self.registry.manifest_by_symbol(&dependency.id)
115                && !dependency_satisfied(loaded_manifest, dependency)
116            {
117                return Err(Error::DependencyVersionMismatch {
118                    lib: manifest.id.clone(),
119                    dependency: dependency.id.clone(),
120                    required: dependency
121                        .minimum_version
122                        .clone()
123                        .unwrap_or_else(|| Version(String::from("0"))),
124                    loaded: loaded_manifest.version.clone(),
125                });
126            }
127        }
128        let trusted = matches!(manifest.target, LibTarget::HostRegistered);
129        let mut txn = self.registry.try_begin_load(manifest, trusted)?;
130        {
131            let mut load_cx = self.load_cx();
132            let mut linker = txn.linker();
133            lib.load(&mut load_cx, &mut linker)?;
134        }
135        let lib_id = self.registry.commit_load(txn)?;
136        let loaded = self
137            .registry
138            .libs()
139            .iter()
140            .find(|loaded| loaded.id == lib_id)
141            .cloned();
142        if let Some(loaded) = loaded {
143            let claim_ids = publish_loaded_lib_claims(self, &loaded)?;
144            self.record_load_claims(lib_id, claim_ids);
145        }
146        Ok(lib_id)
147    }
148
149    /// Loads several libraries in dependency order, returning their ids in load
150    /// order.
151    pub fn load_libs(&mut self, libs: &[&dyn Lib]) -> Result<Vec<LibId>> {
152        let manifests = libs.iter().map(|lib| lib.manifest()).collect::<Vec<_>>();
153        let ordered = self.registry.dependency_order(&manifests)?;
154        let mut ids = Vec::with_capacity(ordered.len());
155
156        for manifest in ordered {
157            let lib = libs
158                .iter()
159                .find(|candidate| candidate.manifest().id == manifest.id)
160                .ok_or_else(|| {
161                    Error::Lib(format!("missing lib implementation for {}", manifest.id))
162                })?;
163            ids.push(self.load_lib(*lib)?);
164        }
165
166        Ok(ids)
167    }
168
169    /// Unloads a loaded library by id and retracts the claims published for it.
170    ///
171    /// Already-live values may continue to exist, but fresh registry resolution
172    /// for the unloaded library's exports is removed.
173    pub fn unload_lib(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
174        let unloaded = self.registry.unload(lib_id)?;
175        self.remove_load_claims(&unloaded);
176        Ok(unloaded)
177    }
178
179    /// Unloads a library and every loaded dependent in reverse dependency
180    /// order, retracting the claims published for each unloaded lib.
181    pub fn unload_lib_cascade(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
182        let unloaded = self.registry.unload_cascade(lib_id)?;
183        self.remove_load_claims(&unloaded);
184        Ok(unloaded)
185    }
186}