Skip to main content

sim_kernel/library/registry/
unload.rs

1use std::collections::BTreeSet;
2
3use crate::{
4    Error, LibId, Result, RuntimeId, Symbol,
5    library::{ExportState, LoadedLib},
6};
7
8use super::{
9    DeltaRange, LoadDelta, Registry,
10    catalog::{
11        SEQ_CASE, SEQ_CLASS, SEQ_CODEC, SEQ_FUNCTION, SEQ_LIB, SEQ_MACRO, SEQ_NUMBER_DOMAIN,
12        SEQ_SHAPE, SEQ_SITE, export_key, export_record_key, exports_table, lib_key, libs_table,
13        number_binary_op_row, number_ops_table, number_reduction_op_row, number_unary_op_row,
14        plain_value_key, promotion_rule_row, promotion_rules_table, runtime_key, runtime_table,
15        test_key, tests_table, value_number_binary_op_row, value_number_reduction_op_row,
16        value_number_unary_op_row, value_promotion_rule_row, value_promotion_rules_table,
17    },
18};
19
20impl Registry {
21    /// Unloads a single library by stable id.
22    ///
23    /// This is a bare unload: if any loaded library depends on `lib_id`, the
24    /// call refuses with [`Error::LibHasDependents`]. Passing an absent id is a
25    /// no-op and returns an empty list. On success the returned list contains
26    /// the unloaded id.
27    pub fn unload(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
28        let Some(lib) = self.loaded_lib(lib_id) else {
29            return Ok(Vec::new());
30        };
31        let dependents = self.dependent_ids_for(lib_id);
32        if !dependents.is_empty() {
33            return Err(Error::LibHasDependents {
34                lib: lib.manifest.id.clone(),
35                dependents: self.symbols_for_lib_ids(&dependents),
36            });
37        }
38        Ok(self.unload_one(lib_id)?.into_iter().collect())
39    }
40
41    /// Unloads a library and its dependents in reverse load order.
42    ///
43    /// Passing an absent id is a no-op and returns an empty list. The returned
44    /// ids are ordered by the actual unload sequence, with dependents before
45    /// their dependencies.
46    pub fn unload_cascade(&mut self, lib_id: LibId) -> Result<Vec<LibId>> {
47        if self.loaded_lib(lib_id).is_none() {
48            return Ok(Vec::new());
49        }
50        let mut selected = BTreeSet::new();
51        self.collect_dependents(lib_id, &mut selected);
52        selected.insert(lib_id);
53
54        let mut ordered = self
55            .libs
56            .iter()
57            .filter_map(|loaded| selected.contains(&loaded.id).then_some(loaded.id))
58            .collect::<Vec<_>>();
59        ordered.reverse();
60
61        let mut unloaded = Vec::with_capacity(ordered.len());
62        for id in ordered {
63            if let Some(id) = self.unload_one(id)? {
64                unloaded.push(id);
65            }
66        }
67        Ok(unloaded)
68    }
69
70    fn unload_one(&mut self, lib_id: LibId) -> Result<Option<LibId>> {
71        let Some(index) = self.libs.iter().position(|loaded| loaded.id == lib_id) else {
72            return Ok(None);
73        };
74        let loaded = self.libs.remove(index);
75        let delta = self.load_deltas.remove(&lib_id).unwrap_or_default();
76        self.load_dependencies.remove(&lib_id);
77        for dependencies in self.load_dependencies.values_mut() {
78            dependencies.remove(&lib_id);
79        }
80
81        self.delete_loaded_catalog_rows(&loaded);
82        self.delete_registered_tests_for_lib(&loaded.manifest.id);
83        self.remove_number_delta(delta);
84        self.rebuild_number_catalog_rows();
85        self.rebuild_projection_caches_from_catalog();
86        self.recompute_sequence_rows()?;
87        Ok(Some(lib_id))
88    }
89
90    fn loaded_lib(&self, lib_id: LibId) -> Option<&LoadedLib> {
91        self.libs.iter().find(|loaded| loaded.id == lib_id)
92    }
93
94    fn dependent_ids_for(&self, lib_id: LibId) -> Vec<LibId> {
95        self.load_dependencies
96            .iter()
97            .filter_map(|(dependent, dependencies)| {
98                dependencies.contains(&lib_id).then_some(*dependent)
99            })
100            .filter(|dependent| self.loaded_lib(*dependent).is_some())
101            .collect()
102    }
103
104    fn collect_dependents(&self, lib_id: LibId, selected: &mut BTreeSet<LibId>) {
105        for dependent in self.dependent_ids_for(lib_id) {
106            if selected.insert(dependent) {
107                self.collect_dependents(dependent, selected);
108            }
109        }
110    }
111
112    fn symbols_for_lib_ids(&self, ids: &[LibId]) -> Vec<Symbol> {
113        ids.iter()
114            .filter_map(|id| {
115                self.loaded_lib(*id)
116                    .map(|loaded| loaded.manifest.id.clone())
117            })
118            .collect()
119    }
120
121    fn delete_loaded_catalog_rows(&mut self, loaded: &LoadedLib) {
122        self.catalog
123            .delete_row(&libs_table(), &lib_key(&loaded.manifest.id));
124        for record in &loaded.exports {
125            self.catalog.delete_row(
126                &exports_table(),
127                &export_catalog_key(&loaded.manifest.id, record),
128            );
129            if let ExportState::Resolved { id } = record.state {
130                self.catalog
131                    .delete_row(&runtime_table(), &runtime_catalog_key(id, &record.symbol));
132            }
133        }
134    }
135
136    fn delete_registered_tests_for_lib(&mut self, lib: &Symbol) {
137        let test_kind = Symbol::new("test");
138        let symbols = self.tests_by_lib.get(lib).cloned().unwrap_or_default();
139        for symbol in symbols {
140            self.catalog.delete_row(&tests_table(), &test_key(&symbol));
141            self.catalog.delete_row(
142                &exports_table(),
143                &export_record_key(lib, &test_kind, &symbol),
144            );
145        }
146    }
147
148    fn remove_number_delta(&mut self, removed: LoadDelta) {
149        drain_range(&mut self.number_unary_ops, removed.number_unary_ops);
150        drain_range(&mut self.number_reduction_ops, removed.number_reduction_ops);
151        drain_range(&mut self.number_binary_ops, removed.number_binary_ops);
152        drain_range(
153            &mut self.value_number_unary_ops,
154            removed.value_number_unary_ops,
155        );
156        drain_range(
157            &mut self.value_number_reduction_ops,
158            removed.value_number_reduction_ops,
159        );
160        drain_range(
161            &mut self.value_number_binary_ops,
162            removed.value_number_binary_ops,
163        );
164        drain_range(&mut self.promotion_rules, removed.promotion_rules);
165        drain_range(
166            &mut self.value_promotion_rules,
167            removed.value_promotion_rules,
168        );
169
170        for delta in self.load_deltas.values_mut() {
171            delta
172                .number_unary_ops
173                .adjust_after_removed(removed.number_unary_ops);
174            delta
175                .number_reduction_ops
176                .adjust_after_removed(removed.number_reduction_ops);
177            delta
178                .number_binary_ops
179                .adjust_after_removed(removed.number_binary_ops);
180            delta
181                .value_number_unary_ops
182                .adjust_after_removed(removed.value_number_unary_ops);
183            delta
184                .value_number_reduction_ops
185                .adjust_after_removed(removed.value_number_reduction_ops);
186            delta
187                .value_number_binary_ops
188                .adjust_after_removed(removed.value_number_binary_ops);
189            delta
190                .promotion_rules
191                .adjust_after_removed(removed.promotion_rules);
192            delta
193                .value_promotion_rules
194                .adjust_after_removed(removed.value_promotion_rules);
195        }
196    }
197
198    fn rebuild_number_catalog_rows(&mut self) {
199        clear_table(self, &number_ops_table());
200        clear_table(self, &promotion_rules_table());
201        clear_table(self, &value_promotion_rules_table());
202
203        for (index, op) in self.number_unary_ops.iter().cloned().enumerate() {
204            self.catalog.put_row(number_unary_op_row(index as u64, op));
205        }
206        for (index, op) in self.number_reduction_ops.iter().cloned().enumerate() {
207            self.catalog
208                .put_row(number_reduction_op_row(index as u64, op));
209        }
210        for (index, op) in self.number_binary_ops.iter().cloned().enumerate() {
211            self.catalog.put_row(number_binary_op_row(index as u64, op));
212        }
213        for (index, op) in self.value_number_unary_ops.iter().cloned().enumerate() {
214            self.catalog
215                .put_row(value_number_unary_op_row(index as u64, op));
216        }
217        for (index, op) in self.value_number_reduction_ops.iter().cloned().enumerate() {
218            self.catalog
219                .put_row(value_number_reduction_op_row(index as u64, op));
220        }
221        for (index, op) in self.value_number_binary_ops.iter().cloned().enumerate() {
222            self.catalog
223                .put_row(value_number_binary_op_row(index as u64, op));
224        }
225        for (index, rule) in self.promotion_rules.iter().cloned().enumerate() {
226            self.catalog.put_row(promotion_rule_row(index as u64, rule));
227        }
228        for (index, rule) in self.value_promotion_rules.iter().cloned().enumerate() {
229            self.catalog
230                .put_row(value_promotion_rule_row(index as u64, rule));
231        }
232    }
233
234    fn recompute_sequence_rows(&mut self) -> Result<()> {
235        for kind in [
236            SEQ_LIB,
237            SEQ_CLASS,
238            SEQ_FUNCTION,
239            SEQ_MACRO,
240            SEQ_CASE,
241            SEQ_SHAPE,
242            SEQ_CODEC,
243            SEQ_NUMBER_DOMAIN,
244            SEQ_SITE,
245        ] {
246            self.set_catalog_sequence_next(kind, self.sequence_next_after_unload(kind))?;
247        }
248        Ok(())
249    }
250
251    fn sequence_next_after_unload(&self, kind: &'static str) -> u64 {
252        let symbol = Symbol::new(kind);
253        let mut next = self
254            .load_deltas
255            .values()
256            .filter_map(|delta| delta.sequence_after.get(&symbol).copied())
257            .max()
258            .unwrap_or(1);
259        next = next.max(match kind {
260            SEQ_LIB => max_id_next(self.libs.iter().map(|loaded| loaded.id.0)),
261            SEQ_CLASS => max_id_next(self.class_symbol_cache.values().map(|id| id.0)),
262            SEQ_FUNCTION => max_id_next(self.function_symbol_cache.values().map(|id| id.0)),
263            SEQ_MACRO => max_id_next(self.macro_symbol_cache.values().map(|id| id.0)),
264            SEQ_SHAPE => max_id_next(self.shape_symbol_cache.values().map(|id| id.0)),
265            SEQ_CODEC => max_id_next(self.codec_symbol_cache.values().map(|id| id.0)),
266            SEQ_NUMBER_DOMAIN => {
267                max_id_next(self.number_domain_symbol_cache.values().map(|id| id.0))
268            }
269            SEQ_SITE => max_id_next(self.site_symbol_cache.values().map(|id| id.0)),
270            SEQ_CASE => 1,
271            _ => 1,
272        });
273        next
274    }
275}
276
277fn export_catalog_key(lib: &Symbol, record: &crate::ExportRecord) -> Symbol {
278    match record.state {
279        ExportState::Resolved { .. } => export_key(record.kind.symbol(), &record.symbol),
280        _ => export_record_key(lib, record.kind.symbol(), &record.symbol),
281    }
282}
283
284fn runtime_catalog_key(runtime_id: RuntimeId, symbol: &Symbol) -> Symbol {
285    match runtime_id {
286        RuntimeId::Class(id) => runtime_key(&Symbol::new(SEQ_CLASS), u64::from(id.0)),
287        RuntimeId::Function(id) => runtime_key(&Symbol::new(SEQ_FUNCTION), u64::from(id.0)),
288        RuntimeId::Macro(id) => runtime_key(&Symbol::new(SEQ_MACRO), u64::from(id.0)),
289        RuntimeId::Shape(id) => runtime_key(&Symbol::new(SEQ_SHAPE), u64::from(id.0)),
290        RuntimeId::Codec(id) => runtime_key(&Symbol::new(SEQ_CODEC), u64::from(id.0)),
291        RuntimeId::NumberDomain(id) => {
292            runtime_key(&Symbol::new(SEQ_NUMBER_DOMAIN), u64::from(id.0))
293        }
294        RuntimeId::Site(id) => runtime_key(&Symbol::new(SEQ_SITE), u64::from(id.0)),
295        RuntimeId::Value => plain_value_key(symbol),
296    }
297}
298
299fn drain_range<T>(items: &mut Vec<T>, range: DeltaRange) {
300    if range.is_empty() {
301        return;
302    }
303    items.drain(range.start..range.end());
304}
305
306fn clear_table(registry: &mut Registry, table: &Symbol) {
307    let keys = registry
308        .catalog
309        .rows(table)
310        .map(|rows| rows.keys().cloned().collect::<Vec<_>>())
311        .unwrap_or_default();
312    for key in keys {
313        registry.catalog.delete_row(table, &key);
314    }
315}
316
317fn max_id_next(ids: impl Iterator<Item = u32>) -> u64 {
318    ids.map(u64::from)
319        .max()
320        .and_then(|id| id.checked_add(1))
321        .unwrap_or(1)
322}