1use alloc::{
2 collections::{BTreeMap, BTreeSet},
3 string::String,
4 sync::Arc,
5 vec::Vec,
6};
7
8use vm_core::{
9 AdviceMap, Kernel,
10 crypto::hash::RpoDigest,
11 mast::{MastForest, MastNodeId},
12 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
13};
14
15use crate::ast::QualifiedProcedureName;
16
17mod error;
18mod module;
19mod namespace;
20mod path;
21mod version;
22
23pub use module::{ModuleInfo, ProcedureInfo};
24
25pub use self::{
26 error::LibraryError,
27 namespace::{LibraryNamespace, LibraryNamespaceError},
28 path::{LibraryPath, LibraryPathComponent, PathError},
29 version::{Version, VersionError},
30};
31
32#[cfg(test)]
33mod tests;
34
35#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Library {
44 digest: RpoDigest,
47 exports: BTreeMap<QualifiedProcedureName, MastNodeId>,
57 mast_forest: Arc<MastForest>,
59}
60
61impl AsRef<Library> for Library {
62 #[inline(always)]
63 fn as_ref(&self) -> &Library {
64 self
65 }
66}
67
68impl Library {
71 pub fn new(
78 mast_forest: Arc<MastForest>,
79 exports: BTreeMap<QualifiedProcedureName, MastNodeId>,
80 ) -> Result<Self, LibraryError> {
81 if exports.is_empty() {
82 return Err(LibraryError::NoExport);
83 }
84 for (fqn, &proc_body_id) in exports.iter() {
85 if !mast_forest.is_procedure_root(proc_body_id) {
86 return Err(LibraryError::NoProcedureRootForExport { procedure_path: fqn.clone() });
87 }
88 }
89
90 let digest = compute_content_hash(&exports, &mast_forest);
91
92 Ok(Self { digest, exports, mast_forest })
93 }
94
95 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
98 let mut mast_forest = (*self.mast_forest).clone();
99 mast_forest.advice_map_mut().extend(advice_map);
100 Self {
101 mast_forest: Arc::new(mast_forest),
102 ..self
103 }
104 }
105}
106
107impl Library {
110 pub fn digest(&self) -> &RpoDigest {
112 &self.digest
113 }
114
115 pub fn exports(&self) -> impl Iterator<Item = &QualifiedProcedureName> {
117 self.exports.keys()
118 }
119
120 pub fn num_exports(&self) -> usize {
122 self.exports.len()
123 }
124
125 pub fn get_export_node_id(&self, proc_name: &QualifiedProcedureName) -> MastNodeId {
130 *self.exports.get(proc_name).expect("procedure not exported from the library")
131 }
132
133 pub fn is_reexport(&self, proc_name: &QualifiedProcedureName) -> bool {
135 self.exports
136 .get(proc_name)
137 .map(|&node_id| self.mast_forest[node_id].is_external())
138 .unwrap_or(false)
139 }
140
141 pub fn mast_forest(&self) -> &Arc<MastForest> {
143 &self.mast_forest
144 }
145}
146
147impl Library {
149 pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
151 let mut modules_by_path: BTreeMap<LibraryPath, ModuleInfo> = BTreeMap::new();
152
153 for (proc_name, &proc_root_node_id) in self.exports.iter() {
154 modules_by_path
155 .entry(proc_name.module.clone())
156 .and_modify(|compiled_module| {
157 let proc_digest = self.mast_forest[proc_root_node_id].digest();
158 compiled_module.add_procedure(proc_name.name.clone(), proc_digest);
159 })
160 .or_insert_with(|| {
161 let mut module_info = ModuleInfo::new(proc_name.module.clone());
162
163 let proc_digest = self.mast_forest[proc_root_node_id].digest();
164 module_info.add_procedure(proc_name.name.clone(), proc_digest);
165
166 module_info
167 });
168 }
169
170 modules_by_path.into_values()
171 }
172}
173
174impl Serializable for Library {
175 fn write_into<W: ByteWriter>(&self, target: &mut W) {
176 let Self { digest: _, exports, mast_forest } = self;
177
178 mast_forest.write_into(target);
179
180 target.write_usize(exports.len());
181 for (proc_name, proc_node_id) in exports {
182 proc_name.module.write_into(target);
183 proc_name.name.write_into(target);
184 target.write_u32(proc_node_id.as_u32());
185 }
186 }
187}
188
189impl Deserializable for Library {
190 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
191 let mast_forest = Arc::new(MastForest::read_from(source)?);
192
193 let num_exports = source.read_usize()?;
194 if num_exports == 0 {
195 return Err(DeserializationError::InvalidValue(String::from("No exported procedures")));
196 };
197 let mut exports = BTreeMap::new();
198 for _ in 0..num_exports {
199 let proc_module = source.read()?;
200 let proc_name = source.read()?;
201 let proc_name = QualifiedProcedureName::new(proc_module, proc_name);
202 let proc_node_id = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
203
204 exports.insert(proc_name, proc_node_id);
205 }
206
207 let digest = compute_content_hash(&exports, &mast_forest);
208
209 Ok(Self { digest, exports, mast_forest })
210 }
211}
212
213fn compute_content_hash(
214 exports: &BTreeMap<QualifiedProcedureName, MastNodeId>,
215 mast_forest: &MastForest,
216) -> RpoDigest {
217 let digests = BTreeSet::from_iter(exports.values().map(|&id| mast_forest[id].digest()));
218 digests
219 .into_iter()
220 .reduce(|a, b| vm_core::crypto::hash::Rpo256::merge(&[a, b]))
221 .unwrap()
222}
223
224#[cfg(feature = "std")]
225mod use_std_library {
226 use std::{fs, io, path::Path};
227
228 use miette::Report;
229 use vm_core::utils::ReadAdapter;
230
231 use super::*;
232 use crate::Assembler;
233
234 impl Library {
235 pub const LIBRARY_EXTENSION: &'static str = "masl";
237
238 pub fn write_to_file(&self, path: impl AsRef<Path>) -> io::Result<()> {
244 let path = path.as_ref();
245
246 if let Some(dir) = path.parent() {
247 fs::create_dir_all(dir)?;
248 }
249
250 std::panic::catch_unwind(|| {
254 let mut file = fs::File::create(path)?;
255 self.write_into(&mut file);
256 Ok(())
257 })
258 .map_err(|p| {
259 match p.downcast::<io::Error>() {
260 Ok(err) => unsafe { core::ptr::read(&*err) },
262 Err(err) => std::panic::resume_unwind(err),
263 }
264 })?
265 }
266
267 pub fn from_dir(
300 path: impl AsRef<Path>,
301 namespace: LibraryNamespace,
302 assembler: Assembler,
303 ) -> Result<Self, Report> {
304 let path = path.as_ref();
305
306 let src_manager = assembler.source_manager();
307 let modules = crate::parser::read_modules_from_dir(namespace, path, &src_manager)?;
308 assembler.assemble_library(modules)
309 }
310
311 pub fn deserialize_from_file(path: impl AsRef<Path>) -> Result<Self, DeserializationError> {
312 let path = path.as_ref();
313 let mut file = fs::File::open(path).map_err(|err| {
314 DeserializationError::InvalidValue(format!(
315 "failed to open file at {}: {err}",
316 path.to_string_lossy()
317 ))
318 })?;
319 let mut adapter = ReadAdapter::new(&mut file);
320
321 Self::read_from(&mut adapter)
322 }
323 }
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
336pub struct KernelLibrary {
337 kernel: Kernel,
338 kernel_info: ModuleInfo,
339 library: Library,
340}
341
342impl AsRef<Library> for KernelLibrary {
343 #[inline(always)]
344 fn as_ref(&self) -> &Library {
345 &self.library
346 }
347}
348
349impl KernelLibrary {
350 pub fn kernel(&self) -> &Kernel {
352 &self.kernel
353 }
354
355 pub fn mast_forest(&self) -> &Arc<MastForest> {
357 self.library.mast_forest()
358 }
359
360 pub fn into_parts(self) -> (Kernel, ModuleInfo, Arc<MastForest>) {
362 (self.kernel, self.kernel_info, self.library.mast_forest)
363 }
364}
365
366impl TryFrom<Library> for KernelLibrary {
367 type Error = LibraryError;
368
369 fn try_from(library: Library) -> Result<Self, Self::Error> {
370 let kernel_path = LibraryPath::from(LibraryNamespace::Kernel);
371 let mut proc_digests = Vec::with_capacity(library.exports.len());
372
373 let mut kernel_module = ModuleInfo::new(kernel_path.clone());
374
375 for (proc_path, &proc_node_id) in library.exports.iter() {
376 if proc_path.module != kernel_path {
378 return Err(LibraryError::InvalidKernelExport {
379 procedure_path: proc_path.clone(),
380 });
381 }
382
383 let proc_digest = library.mast_forest[proc_node_id].digest();
384 proc_digests.push(proc_digest);
385 kernel_module.add_procedure(proc_path.name.clone(), proc_digest);
386 }
387
388 let kernel = Kernel::new(&proc_digests).map_err(LibraryError::KernelConversion)?;
389
390 Ok(Self {
391 kernel,
392 kernel_info: kernel_module,
393 library,
394 })
395 }
396}
397
398impl Serializable for KernelLibrary {
399 fn write_into<W: ByteWriter>(&self, target: &mut W) {
400 let Self { kernel: _, kernel_info: _, library } = self;
401
402 library.write_into(target);
403 }
404}
405
406impl Deserializable for KernelLibrary {
407 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
408 let library = Library::read_from(source)?;
409
410 Self::try_from(library).map_err(|err| {
411 DeserializationError::InvalidValue(format!(
412 "Failed to deserialize kernel library: {err}"
413 ))
414 })
415 }
416}
417
418#[cfg(feature = "std")]
419mod use_std_kernel {
420 use std::{io, path::Path};
421
422 use super::*;
423 use crate::{Assembler, diagnostics::Report};
424
425 impl KernelLibrary {
426 pub fn write_to_file(&self, path: impl AsRef<Path>) -> io::Result<()> {
428 self.library.write_to_file(path)
429 }
430
431 pub fn from_dir(
445 sys_module_path: impl AsRef<Path>,
446 lib_dir: Option<impl AsRef<Path>>,
447 mut assembler: Assembler,
448 ) -> Result<Self, Report> {
449 if let Some(lib_dir) = lib_dir {
451 let lib_dir = lib_dir.as_ref();
452 let namespace = LibraryNamespace::new("kernel").expect("invalid namespace");
453 assembler.add_modules_from_dir(namespace, lib_dir)?;
454 }
455
456 assembler.assemble_kernel(sys_module_path.as_ref())
457 }
458 }
459}