miden_assembly_syntax/library/
mod.rs1use alloc::{
2 collections::{BTreeMap, BTreeSet},
3 string::String,
4 sync::Arc,
5 vec::Vec,
6};
7
8use miden_core::{
9 AdviceMap, Kernel, Word,
10 mast::{MastForest, MastNodeId},
11 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
12};
13
14use crate::ast::QualifiedProcedureName;
15
16mod error;
17mod module;
18mod namespace;
19mod path;
20
21pub use module::{ModuleInfo, ProcedureInfo};
22pub use semver::{Error as VersionError, Version};
23
24pub use self::{
25 error::LibraryError,
26 namespace::{LibraryNamespace, LibraryNamespaceError},
27 path::{LibraryPath, LibraryPathComponent, PathError},
28};
29
30#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Library {
39 digest: Word,
42 exports: BTreeMap<QualifiedProcedureName, MastNodeId>,
52 mast_forest: Arc<MastForest>,
54}
55
56impl AsRef<Library> for Library {
57 #[inline(always)]
58 fn as_ref(&self) -> &Library {
59 self
60 }
61}
62
63impl Library {
66 pub fn new(
73 mast_forest: Arc<MastForest>,
74 exports: BTreeMap<QualifiedProcedureName, MastNodeId>,
75 ) -> Result<Self, LibraryError> {
76 if exports.is_empty() {
77 return Err(LibraryError::NoExport);
78 }
79 for (fqn, &proc_body_id) in exports.iter() {
80 if !mast_forest.is_procedure_root(proc_body_id) {
81 return Err(LibraryError::NoProcedureRootForExport { procedure_path: fqn.clone() });
82 }
83 }
84
85 let digest = compute_content_hash(&exports, &mast_forest);
86
87 Ok(Self { digest, exports, mast_forest })
88 }
89
90 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
93 let mut mast_forest = (*self.mast_forest).clone();
94 mast_forest.advice_map_mut().extend(advice_map);
95 Self {
96 mast_forest: Arc::new(mast_forest),
97 ..self
98 }
99 }
100}
101
102impl Library {
105 pub fn digest(&self) -> &Word {
107 &self.digest
108 }
109
110 pub fn exports(&self) -> impl Iterator<Item = &QualifiedProcedureName> {
112 self.exports.keys()
113 }
114
115 pub fn num_exports(&self) -> usize {
117 self.exports.len()
118 }
119
120 pub fn get_export_node_id(&self, proc_name: &QualifiedProcedureName) -> MastNodeId {
125 *self.exports.get(proc_name).expect("procedure not exported from the library")
126 }
127
128 pub fn is_reexport(&self, proc_name: &QualifiedProcedureName) -> bool {
130 self.exports
131 .get(proc_name)
132 .map(|&node_id| self.mast_forest[node_id].is_external())
133 .unwrap_or(false)
134 }
135
136 pub fn mast_forest(&self) -> &Arc<MastForest> {
138 &self.mast_forest
139 }
140
141 pub fn get_procedure_root_by_name(
144 &self,
145 proc_name: impl TryInto<QualifiedProcedureName>,
146 ) -> Option<Word> {
147 if let Ok(qualified_proc_name) = proc_name.try_into() {
148 let node_id = self.exports.get(&qualified_proc_name);
149 node_id.map(|id| self.mast_forest()[*id].digest())
150 } else {
151 None
152 }
153 }
154}
155
156impl Library {
158 pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
160 let mut modules_by_path: BTreeMap<LibraryPath, ModuleInfo> = BTreeMap::new();
161
162 for (proc_name, &proc_root_node_id) in self.exports.iter() {
163 modules_by_path
164 .entry(proc_name.module.clone())
165 .and_modify(|compiled_module| {
166 let proc_digest = self.mast_forest[proc_root_node_id].digest();
167 compiled_module.add_procedure(proc_name.name.clone(), proc_digest);
168 })
169 .or_insert_with(|| {
170 let mut module_info = ModuleInfo::new(proc_name.module.clone());
171
172 let proc_digest = self.mast_forest[proc_root_node_id].digest();
173 module_info.add_procedure(proc_name.name.clone(), proc_digest);
174
175 module_info
176 });
177 }
178
179 modules_by_path.into_values()
180 }
181}
182
183impl Serializable for Library {
184 fn write_into<W: ByteWriter>(&self, target: &mut W) {
185 let Self { digest: _, exports, mast_forest } = self;
186
187 mast_forest.write_into(target);
188
189 target.write_usize(exports.len());
190 for (proc_name, proc_node_id) in exports {
191 proc_name.module.write_into(target);
192 proc_name.name.write_into(target);
193 target.write_u32(proc_node_id.as_u32());
194 }
195 }
196}
197
198impl Deserializable for Library {
199 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
200 let mast_forest = Arc::new(MastForest::read_from(source)?);
201
202 let num_exports = source.read_usize()?;
203 if num_exports == 0 {
204 return Err(DeserializationError::InvalidValue(String::from("No exported procedures")));
205 };
206 let mut exports = BTreeMap::new();
207 for _ in 0..num_exports {
208 let proc_module = source.read()?;
209 let proc_name = source.read()?;
210 let proc_name = QualifiedProcedureName::new(proc_module, proc_name);
211 let proc_node_id = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
212
213 exports.insert(proc_name, proc_node_id);
214 }
215
216 let digest = compute_content_hash(&exports, &mast_forest);
217
218 Ok(Self { digest, exports, mast_forest })
219 }
220}
221
222fn compute_content_hash(
223 exports: &BTreeMap<QualifiedProcedureName, MastNodeId>,
224 mast_forest: &MastForest,
225) -> Word {
226 let digests = BTreeSet::from_iter(exports.values().map(|&id| mast_forest[id].digest()));
227 digests
228 .into_iter()
229 .reduce(|a, b| miden_core::crypto::hash::Rpo256::merge(&[a, b]))
230 .unwrap()
231}
232
233#[cfg(feature = "std")]
234impl Library {
235 pub const LIBRARY_EXTENSION: &'static str = "masl";
237
238 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
244 let path = path.as_ref();
245
246 if let Some(dir) = path.parent() {
247 std::fs::create_dir_all(dir)?;
248 }
249
250 std::panic::catch_unwind(|| {
254 let mut file = std::fs::File::create(path)?;
255 self.write_into(&mut file);
256 Ok(())
257 })
258 .map_err(|p| {
259 match p.downcast::<std::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 deserialize_from_file(
268 path: impl AsRef<std::path::Path>,
269 ) -> Result<Self, DeserializationError> {
270 use miden_core::utils::ReadAdapter;
271
272 let path = path.as_ref();
273 let mut file = std::fs::File::open(path).map_err(|err| {
274 DeserializationError::InvalidValue(format!(
275 "failed to open file at {}: {err}",
276 path.to_string_lossy()
277 ))
278 })?;
279 let mut adapter = ReadAdapter::new(&mut file);
280
281 Self::read_from(&mut adapter)
282 }
283}
284
285#[derive(Debug, Clone, PartialEq, Eq)]
295pub struct KernelLibrary {
296 kernel: Kernel,
297 kernel_info: ModuleInfo,
298 library: Library,
299}
300
301impl AsRef<Library> for KernelLibrary {
302 #[inline(always)]
303 fn as_ref(&self) -> &Library {
304 &self.library
305 }
306}
307
308impl KernelLibrary {
309 pub fn kernel(&self) -> &Kernel {
311 &self.kernel
312 }
313
314 pub fn mast_forest(&self) -> &Arc<MastForest> {
316 self.library.mast_forest()
317 }
318
319 pub fn into_parts(self) -> (Kernel, ModuleInfo, Arc<MastForest>) {
321 (self.kernel, self.kernel_info, self.library.mast_forest)
322 }
323}
324
325impl TryFrom<Library> for KernelLibrary {
326 type Error = LibraryError;
327
328 fn try_from(library: Library) -> Result<Self, Self::Error> {
329 let kernel_path = LibraryPath::from(LibraryNamespace::Kernel);
330 let mut proc_digests = Vec::with_capacity(library.exports.len());
331
332 let mut kernel_module = ModuleInfo::new(kernel_path.clone());
333
334 for (proc_path, &proc_node_id) in library.exports.iter() {
335 if proc_path.module != kernel_path {
337 return Err(LibraryError::InvalidKernelExport {
338 procedure_path: proc_path.clone(),
339 });
340 }
341
342 let proc_digest = library.mast_forest[proc_node_id].digest();
343 proc_digests.push(proc_digest);
344 kernel_module.add_procedure(proc_path.name.clone(), proc_digest);
345 }
346
347 let kernel = Kernel::new(&proc_digests).map_err(LibraryError::KernelConversion)?;
348
349 Ok(Self {
350 kernel,
351 kernel_info: kernel_module,
352 library,
353 })
354 }
355}
356
357impl Serializable for KernelLibrary {
358 fn write_into<W: ByteWriter>(&self, target: &mut W) {
359 let Self { kernel: _, kernel_info: _, library } = self;
360
361 library.write_into(target);
362 }
363}
364
365impl Deserializable for KernelLibrary {
366 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
367 let library = Library::read_from(source)?;
368
369 Self::try_from(library).map_err(|err| {
370 DeserializationError::InvalidValue(format!(
371 "Failed to deserialize kernel library: {err}"
372 ))
373 })
374 }
375}
376
377#[cfg(feature = "std")]
378impl KernelLibrary {
379 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
381 self.library.write_to_file(path)
382 }
383}