Skip to main content

sim_kernel/library/
transaction.rs

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