Skip to main content

sim_kernel/library/
transaction.rs

1use std::collections::BTreeMap;
2
3use crate::{
4    error::Result,
5    id::{
6        ClassId, CodecId, FunctionId, LibId, MacroId, NumberDomainId, RuntimeId, ShapeId, SiteId,
7        Symbol,
8    },
9    library::{Export, ExportKind, ExportRecord, ExportState, Registry},
10    number_domain::{
11        NumberBinaryOp, NumberReductionOp, NumberUnaryOp, ValueNumberBinaryOp,
12        ValueNumberReductionOp, ValueNumberUnaryOp, ValuePromotionRule,
13    },
14    value::Value,
15};
16
17#[derive(Default)]
18pub(crate) struct PendingExports {
19    pub(crate) exports: Vec<Export>,
20    pub(crate) export_records: Vec<ExportRecord>,
21    pub(crate) class_value_cache: Vec<(ClassId, Value)>,
22    pub(crate) function_value_cache: Vec<(FunctionId, Value)>,
23    pub(crate) macro_value_cache: Vec<(MacroId, Value)>,
24    pub(crate) shape_value_cache: Vec<(ShapeId, Value)>,
25    pub(crate) codec_value_cache: Vec<(CodecId, Value)>,
26    pub(crate) number_domain_value_cache: Vec<(NumberDomainId, Value)>,
27    pub(crate) site_value_cache: Vec<(SiteId, Value)>,
28    pub(crate) values: Vec<(Symbol, Value)>,
29    pub(crate) promotion_rules: Vec<crate::number_domain::PromotionRule>,
30    pub(crate) value_promotion_rules: Vec<ValuePromotionRule>,
31    pub(crate) number_unary_ops: Vec<NumberUnaryOp>,
32    pub(crate) number_reduction_ops: Vec<NumberReductionOp>,
33    pub(crate) number_binary_ops: Vec<NumberBinaryOp>,
34    pub(crate) value_number_unary_ops: Vec<ValueNumberUnaryOp>,
35    pub(crate) value_number_reduction_ops: Vec<ValueNumberReductionOp>,
36    pub(crate) value_number_binary_ops: Vec<ValueNumberBinaryOp>,
37}
38
39/// An in-progress library load against a private copy of the [`Registry`].
40///
41/// A transaction stages all of a library's registrations on a cloned registry
42/// and an internal pending-exports buffer; nothing reaches the live registry
43/// until [`Registry::commit_load`](crate::library::Registry) succeeds, so a
44/// failed load leaves the registry untouched.
45pub struct LoadTransaction {
46    pub(crate) lib_id: LibId,
47    pub(crate) manifest: crate::library::LibManifest,
48    pub(crate) trusted: bool,
49    pub(crate) registry: Registry,
50    pub(crate) pending: PendingExports,
51    pub(crate) stable_exports: BTreeMap<(ExportKind, Symbol), RuntimeId>,
52}
53
54/// The handle a [`Lib`](crate::library::Lib) uses to register its exports.
55///
56/// A `Linker` borrows the transaction's registry and pending buffer, reserving
57/// stable ids and staging class/function/macro/shape/codec/number-domain/value
58/// exports plus number-domain operators. The kernel defines these registration
59/// contracts; the library calls them to declare its behavior.
60pub struct Linker<'a> {
61    registry: &'a mut Registry,
62    lib: LibId,
63    pending: &'a mut PendingExports,
64    stable_exports: &'a BTreeMap<(ExportKind, Symbol), RuntimeId>,
65}
66
67impl<'a> Linker<'a> {
68    pub(crate) fn new(
69        registry: &'a mut Registry,
70        lib: LibId,
71        pending: &'a mut PendingExports,
72        stable_exports: &'a BTreeMap<(ExportKind, Symbol), RuntimeId>,
73    ) -> Self {
74        Self {
75            registry,
76            lib,
77            pending,
78            stable_exports,
79        }
80    }
81
82    fn stable_id(&self, kind: ExportKind, symbol: &Symbol) -> Option<RuntimeId> {
83        self.stable_exports.get(&(kind, symbol.clone())).copied()
84    }
85
86    /// The id of the library being loaded.
87    pub fn lib_id(&self) -> LibId {
88        self.lib
89    }
90
91    /// Read access to the transaction's working registry.
92    pub fn registry(&self) -> &Registry {
93        self.registry
94    }
95
96    /// Stages a class export under `symbol`, reserving a fresh class id.
97    pub fn class(&mut self, symbol: Symbol) -> Result<ClassId> {
98        let id = match self.stable_id(ExportKind::named(ExportKind::CLASS), &symbol) {
99            Some(RuntimeId::Class(id)) => {
100                self.registry.reserve_class_id(id)?;
101                id
102            }
103            _ => self.registry.try_fresh_class_id()?,
104        };
105        self.pending.exports.push(Export::Class {
106            symbol,
107            class_id: Some(id),
108        });
109        Ok(id)
110    }
111
112    /// Stages a class export under `symbol` using a caller-chosen class id,
113    /// reserving the id sequence up to it.
114    pub fn class_with_id(&mut self, symbol: Symbol, id: ClassId) -> Result<ClassId> {
115        self.registry.reserve_class_id(id)?;
116        self.pending.exports.push(Export::Class {
117            symbol,
118            class_id: Some(id),
119        });
120        Ok(id)
121    }
122
123    /// Stages a class export and binds its runtime value in one step.
124    pub fn class_value(&mut self, symbol: Symbol, value: Value) -> Result<ClassId> {
125        let id = self.class(symbol)?;
126        self.bind_class_value(id, value)?;
127        Ok(id)
128    }
129
130    /// Binds a runtime value to an already-staged class id.
131    pub fn bind_class_value(&mut self, id: ClassId, value: Value) -> Result<()> {
132        self.pending.class_value_cache.push((id, value));
133        Ok(())
134    }
135
136    /// Stages a function export under `symbol`, reserving a fresh function id.
137    pub fn function(&mut self, symbol: Symbol) -> Result<FunctionId> {
138        let id = match self.stable_id(ExportKind::named(ExportKind::FUNCTION), &symbol) {
139            Some(RuntimeId::Function(id)) => {
140                self.registry.reserve_function_id(id)?;
141                id
142            }
143            _ => self.registry.try_fresh_function_id()?,
144        };
145        self.pending.exports.push(Export::Function {
146            symbol,
147            function_id: Some(id),
148        });
149        Ok(id)
150    }
151
152    /// Stages a function export and binds its runtime value in one step.
153    pub fn function_value(&mut self, symbol: Symbol, value: Value) -> Result<FunctionId> {
154        let id = self.function(symbol)?;
155        self.bind_function_value(id, value)?;
156        Ok(id)
157    }
158
159    /// Binds a runtime value to an already-staged function id.
160    pub fn bind_function_value(&mut self, id: FunctionId, value: Value) -> Result<()> {
161        self.pending.function_value_cache.push((id, value));
162        Ok(())
163    }
164
165    /// Stages a macro export under `symbol`, reserving a fresh macro id.
166    pub fn macro_export(&mut self, symbol: Symbol) -> Result<MacroId> {
167        let id = match self.stable_id(ExportKind::named(ExportKind::MACRO), &symbol) {
168            Some(RuntimeId::Macro(id)) => {
169                self.registry.reserve_macro_id(id)?;
170                id
171            }
172            _ => self.registry.try_fresh_macro_id()?,
173        };
174        self.pending.exports.push(Export::Macro {
175            symbol,
176            macro_id: Some(id),
177        });
178        Ok(id)
179    }
180
181    /// Stages a macro export and binds its runtime value in one step.
182    pub fn macro_value(&mut self, symbol: Symbol, value: Value) -> Result<MacroId> {
183        let id = self.macro_export(symbol)?;
184        self.pending.macro_value_cache.push((id, value));
185        Ok(id)
186    }
187
188    /// Stages a shape export under `symbol`, reserving a fresh shape id.
189    pub fn shape(&mut self, symbol: Symbol) -> Result<ShapeId> {
190        let id = match self.stable_id(ExportKind::named(ExportKind::SHAPE), &symbol) {
191            Some(RuntimeId::Shape(id)) => {
192                self.registry.reserve_shape_id(id)?;
193                id
194            }
195            _ => self.registry.try_fresh_shape_id()?,
196        };
197        self.pending.exports.push(Export::Shape {
198            symbol,
199            shape_id: Some(id),
200        });
201        Ok(id)
202    }
203
204    /// Stages a shape export and binds its runtime value in one step.
205    pub fn shape_value(&mut self, symbol: Symbol, value: Value) -> Result<ShapeId> {
206        let id = self.shape(symbol)?;
207        self.pending.shape_value_cache.push((id, value));
208        Ok(id)
209    }
210
211    /// Stages a codec export under `symbol`, reserving a fresh codec id.
212    pub fn codec(&mut self, symbol: Symbol) -> Result<CodecId> {
213        let id = match self.stable_id(ExportKind::named(ExportKind::CODEC), &symbol) {
214            Some(RuntimeId::Codec(id)) => {
215                self.registry.reserve_codec_id(id)?;
216                id
217            }
218            _ => self.registry.try_fresh_codec_id()?,
219        };
220        self.pending.exports.push(Export::Codec {
221            symbol,
222            codec_id: Some(id),
223        });
224        Ok(id)
225    }
226
227    /// Stages a codec export and binds its runtime value in one step.
228    pub fn codec_value(&mut self, symbol: Symbol, value: Value) -> Result<CodecId> {
229        let id = self.codec(symbol)?;
230        self.pending.codec_value_cache.push((id, value));
231        Ok(id)
232    }
233
234    /// Stages a number-domain export under `symbol`, reserving a fresh id.
235    pub fn number_domain(&mut self, symbol: Symbol) -> Result<NumberDomainId> {
236        let id = match self.stable_id(ExportKind::named(ExportKind::NUMBER_DOMAIN), &symbol) {
237            Some(RuntimeId::NumberDomain(id)) => {
238                self.registry.reserve_number_domain_id(id)?;
239                id
240            }
241            _ => self.registry.try_fresh_number_domain_id()?,
242        };
243        self.pending.exports.push(Export::NumberDomain {
244            symbol,
245            number_domain_id: Some(id),
246        });
247        Ok(id)
248    }
249
250    /// Stages a number-domain export and binds its runtime value in one step.
251    pub fn number_domain_value(&mut self, symbol: Symbol, value: Value) -> Result<NumberDomainId> {
252        let id = self.number_domain(symbol)?;
253        self.pending.number_domain_value_cache.push((id, value));
254        Ok(id)
255    }
256
257    /// Stages an opaque site export and binds its runtime value in one step.
258    ///
259    /// The registry stores the value under the export symbol; concrete
260    /// `EvalSite` behavior belongs to libraries that query the site registry.
261    pub fn site_value(&mut self, symbol: Symbol, value: Value) -> Result<RuntimeId> {
262        let site_id = match self.stable_id(ExportKind::named(ExportKind::SITE), &symbol) {
263            Some(RuntimeId::Site(id)) => {
264                self.registry.reserve_site_id(id)?;
265                id
266            }
267            _ => self.registry.try_fresh_site_id()?,
268        };
269        let runtime_id = RuntimeId::Site(site_id);
270        self.pending.exports.push(Export::Site {
271            symbol,
272            runtime_id: Some(runtime_id),
273        });
274        self.pending.site_value_cache.push((site_id, value));
275        Ok(runtime_id)
276    }
277
278    /// Stages a plain value export declaration (without binding a value).
279    pub fn value_export(&mut self, symbol: Symbol) -> Result<()> {
280        self.pending.exports.push(Export::Value { symbol });
281        Ok(())
282    }
283
284    /// Stages a [`Declared`](ExportState::Declared) export record of any kind.
285    pub fn declare_export(&mut self, kind: ExportKind, symbol: Symbol) -> Result<()> {
286        self.pending.export_records.push(ExportRecord {
287            kind,
288            symbol,
289            state: ExportState::Declared,
290        });
291        Ok(())
292    }
293
294    /// Stages an [`Unsupported`](ExportState::Unsupported) export record with a
295    /// reason.
296    pub fn unsupported_export(
297        &mut self,
298        kind: ExportKind,
299        symbol: Symbol,
300        reason: impl Into<String>,
301    ) -> Result<()> {
302        self.pending.export_records.push(ExportRecord {
303            kind,
304            symbol,
305            state: ExportState::Unsupported {
306                reason: reason.into(),
307            },
308        });
309        Ok(())
310    }
311
312    /// Stages an [`Invalid`](ExportState::Invalid) export record with an error.
313    pub fn invalid_export(
314        &mut self,
315        kind: ExportKind,
316        symbol: Symbol,
317        error: impl Into<String>,
318    ) -> Result<()> {
319        self.pending.export_records.push(ExportRecord {
320            kind,
321            symbol,
322            state: ExportState::Invalid {
323                error: error.into(),
324            },
325        });
326        Ok(())
327    }
328
329    /// Stages a plain value export and binds its value in one step.
330    pub fn value(&mut self, symbol: Symbol, value: Value) -> Result<()> {
331        self.value_export(symbol.clone())?;
332        self.pending.values.push((symbol, value));
333        Ok(())
334    }
335
336    /// Stages a typed number binary operator.
337    pub fn number_binary_op(&mut self, op: NumberBinaryOp) {
338        self.pending.number_binary_ops.push(op);
339    }
340
341    /// Stages a value-level number binary operator.
342    pub fn value_number_binary_op(&mut self, op: ValueNumberBinaryOp) {
343        self.pending.value_number_binary_ops.push(op);
344    }
345
346    /// Stages a typed number unary operator.
347    pub fn number_unary_op(&mut self, op: NumberUnaryOp) {
348        self.pending.number_unary_ops.push(op);
349    }
350
351    /// Stages a value-level number unary operator.
352    pub fn value_number_unary_op(&mut self, op: ValueNumberUnaryOp) {
353        self.pending.value_number_unary_ops.push(op);
354    }
355
356    /// Stages a typed number reduction operator.
357    pub fn number_reduction_op(&mut self, op: NumberReductionOp) {
358        self.pending.number_reduction_ops.push(op);
359    }
360
361    /// Stages a value-level number reduction operator.
362    pub fn value_number_reduction_op(&mut self, op: ValueNumberReductionOp) {
363        self.pending.value_number_reduction_ops.push(op);
364    }
365
366    /// Stages a typed number-domain promotion rule.
367    pub fn promotion_rule(&mut self, rule: crate::number_domain::PromotionRule) {
368        self.pending.promotion_rules.push(rule);
369    }
370
371    /// Stages a value-level number-domain promotion rule.
372    pub fn value_promotion_rule(&mut self, rule: ValuePromotionRule) {
373        self.pending.value_promotion_rules.push(rule);
374    }
375}
376
377impl LoadTransaction {
378    /// Borrows a [`Linker`] over this transaction's registry and pending buffer.
379    pub fn linker(&mut self) -> Linker<'_> {
380        Linker::new(
381            &mut self.registry,
382            self.lib_id,
383            &mut self.pending,
384            &self.stable_exports,
385        )
386    }
387}