1use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
2
3use miden_core::{
4 AdviceMap, Kernel, Word,
5 mast::{MastForest, MastNodeId},
6 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use midenc_hir_type::{FunctionType, Type};
9use smallvec::SmallVec;
10
11use crate::ast::QualifiedProcedureName;
12
13mod error;
14mod module;
15mod namespace;
16mod path;
17
18pub use module::{ModuleInfo, ProcedureInfo};
19pub use semver::{Error as VersionError, Version};
20
21pub use self::{
22 error::LibraryError,
23 namespace::{LibraryNamespace, LibraryNamespaceError},
24 path::{LibraryPath, LibraryPathComponent, PathError},
25};
26
27#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct LibraryExport {
33 pub node: MastNodeId,
35 pub name: QualifiedProcedureName,
37 pub signature: Option<FunctionType>,
39}
40
41impl LibraryExport {
42 pub fn new(node: MastNodeId, name: QualifiedProcedureName) -> Self {
44 Self { node, name, signature: None }
45 }
46
47 pub fn with_signature(mut self, signature: FunctionType) -> Self {
49 self.signature = Some(signature);
50 self
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct Library {
63 digest: Word,
66 exports: BTreeMap<QualifiedProcedureName, LibraryExport>,
76 mast_forest: Arc<MastForest>,
78}
79
80impl AsRef<Library> for Library {
81 #[inline(always)]
82 fn as_ref(&self) -> &Library {
83 self
84 }
85}
86
87impl Library {
90 pub fn new(
97 mast_forest: Arc<MastForest>,
98 exports: BTreeMap<QualifiedProcedureName, LibraryExport>,
99 ) -> Result<Self, LibraryError> {
100 if exports.is_empty() {
101 return Err(LibraryError::NoExport);
102 }
103 for LibraryExport { name, node, .. } in exports.values() {
104 if !mast_forest.is_procedure_root(*node) {
105 return Err(LibraryError::NoProcedureRootForExport {
106 procedure_path: name.clone(),
107 });
108 }
109 }
110
111 let digest =
112 mast_forest.compute_nodes_commitment(exports.values().map(|export| &export.node));
113
114 Ok(Self { digest, exports, mast_forest })
115 }
116
117 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
120 let mut mast_forest = (*self.mast_forest).clone();
121 mast_forest.advice_map_mut().extend(advice_map);
122 Self {
123 mast_forest: Arc::new(mast_forest),
124 ..self
125 }
126 }
127}
128
129impl Library {
132 pub fn digest(&self) -> &Word {
134 &self.digest
135 }
136
137 pub fn exports(&self) -> impl Iterator<Item = &LibraryExport> {
139 self.exports.values()
140 }
141
142 pub fn num_exports(&self) -> usize {
144 self.exports.len()
145 }
146
147 pub fn get_export_node_id(&self, proc_name: &QualifiedProcedureName) -> MastNodeId {
152 self.exports
153 .get(proc_name)
154 .expect("procedure not exported from the library")
155 .node
156 }
157
158 pub fn is_reexport(&self, proc_name: &QualifiedProcedureName) -> bool {
160 self.exports
161 .get(proc_name)
162 .map(|export| self.mast_forest[export.node].is_external())
163 .unwrap_or(false)
164 }
165
166 pub fn mast_forest(&self) -> &Arc<MastForest> {
168 &self.mast_forest
169 }
170
171 pub fn get_procedure_root_by_name(
174 &self,
175 proc_name: impl TryInto<QualifiedProcedureName>,
176 ) -> Option<Word> {
177 if let Ok(qualified_proc_name) = proc_name.try_into() {
178 let export = self.exports.get(&qualified_proc_name);
179 export.map(|e| self.mast_forest()[e.node].digest())
180 } else {
181 None
182 }
183 }
184}
185
186impl Library {
188 pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
190 let mut modules_by_path: BTreeMap<LibraryPath, ModuleInfo> = BTreeMap::new();
191
192 for LibraryExport { node, name, signature } in self.exports.values() {
193 modules_by_path
194 .entry(name.module.clone())
195 .and_modify(|compiled_module| {
196 let proc_digest = self.mast_forest[*node].digest();
197 compiled_module.add_procedure(
198 name.name.clone(),
199 proc_digest,
200 signature.clone().map(Arc::new),
201 );
202 })
203 .or_insert_with(|| {
204 let mut module_info = ModuleInfo::new(name.module.clone());
205
206 let proc_digest = self.mast_forest[*node].digest();
207 module_info.add_procedure(
208 name.name.clone(),
209 proc_digest,
210 signature.clone().map(Arc::new),
211 );
212
213 module_info
214 });
215 }
216
217 modules_by_path.into_values()
218 }
219}
220
221#[cfg(feature = "std")]
222impl Library {
223 pub const LIBRARY_EXTENSION: &'static str = "masl";
225
226 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
232 let path = path.as_ref();
233
234 if let Some(dir) = path.parent() {
235 std::fs::create_dir_all(dir)?;
236 }
237
238 std::panic::catch_unwind(|| {
242 let mut file = std::fs::File::create(path)?;
243 self.write_into(&mut file);
244 Ok(())
245 })
246 .map_err(|p| {
247 match p.downcast::<std::io::Error>() {
248 Ok(err) => unsafe { core::ptr::read(&*err) },
250 Err(err) => std::panic::resume_unwind(err),
251 }
252 })?
253 }
254
255 pub fn deserialize_from_file(
256 path: impl AsRef<std::path::Path>,
257 ) -> Result<Self, DeserializationError> {
258 use miden_core::utils::ReadAdapter;
259
260 let path = path.as_ref();
261 let mut file = std::fs::File::open(path).map_err(|err| {
262 DeserializationError::InvalidValue(format!(
263 "failed to open file at {}: {err}",
264 path.to_string_lossy()
265 ))
266 })?;
267 let mut adapter = ReadAdapter::new(&mut file);
268
269 Self::read_from(&mut adapter)
270 }
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
283pub struct KernelLibrary {
284 kernel: Kernel,
285 kernel_info: ModuleInfo,
286 library: Library,
287}
288
289impl AsRef<Library> for KernelLibrary {
290 #[inline(always)]
291 fn as_ref(&self) -> &Library {
292 &self.library
293 }
294}
295
296impl KernelLibrary {
297 pub fn kernel(&self) -> &Kernel {
299 &self.kernel
300 }
301
302 pub fn mast_forest(&self) -> &Arc<MastForest> {
304 self.library.mast_forest()
305 }
306
307 pub fn into_parts(self) -> (Kernel, ModuleInfo, Arc<MastForest>) {
309 (self.kernel, self.kernel_info, self.library.mast_forest)
310 }
311}
312
313impl TryFrom<Library> for KernelLibrary {
314 type Error = LibraryError;
315
316 fn try_from(library: Library) -> Result<Self, Self::Error> {
317 let kernel_path = LibraryPath::from(LibraryNamespace::Kernel);
318 let mut proc_digests = Vec::with_capacity(library.exports.len());
319
320 let mut kernel_module = ModuleInfo::new(kernel_path.clone());
321
322 for export in library.exports.values() {
323 if export.name.module != kernel_path {
325 return Err(LibraryError::InvalidKernelExport {
326 procedure_path: export.name.clone(),
327 });
328 }
329
330 let proc_digest = library.mast_forest[export.node].digest();
331 proc_digests.push(proc_digest);
332 kernel_module.add_procedure(
333 export.name.name.clone(),
334 proc_digest,
335 export.signature.clone().map(Arc::new),
336 );
337 }
338
339 let kernel = Kernel::new(&proc_digests).map_err(LibraryError::KernelConversion)?;
340
341 Ok(Self {
342 kernel,
343 kernel_info: kernel_module,
344 library,
345 })
346 }
347}
348
349#[cfg(feature = "std")]
350impl KernelLibrary {
351 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
353 self.library.write_to_file(path)
354 }
355}
356
357impl Serializable for Library {
362 fn write_into<W: ByteWriter>(&self, target: &mut W) {
363 let Self { digest: _, exports, mast_forest } = self;
364
365 mast_forest.write_into(target);
366
367 target.write_usize(exports.len());
368 for LibraryExport { node, name, signature } in exports.values() {
369 name.module.write_into(target);
370 name.name.write_into(target);
371 target.write_u32(node.as_u32());
372 if let Some(sig) = signature {
373 target.write_bool(true);
374 FunctionTypeSerializer(sig).write_into(target);
375 } else {
376 target.write_bool(false);
377 }
378 }
379 }
380}
381
382impl Deserializable for Library {
384 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
385 let mast_forest = Arc::new(MastForest::read_from(source)?);
386
387 let num_exports = source.read_usize()?;
388 if num_exports == 0 {
389 return Err(DeserializationError::InvalidValue(String::from("No exported procedures")));
390 };
391 let mut exports = BTreeMap::new();
392 for _ in 0..num_exports {
393 let proc_module = source.read()?;
394 let proc_name = source.read()?;
395 let proc_name = QualifiedProcedureName::new(proc_module, proc_name);
396 let node = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
397 let signature = if source.read_bool()? {
398 Some(FunctionTypeDeserializer::read_from(source)?.0)
399 } else {
400 None
401 };
402 let export = LibraryExport { node, name: proc_name.clone(), signature };
403
404 exports.insert(proc_name, export);
405 }
406
407 let digest = mast_forest.compute_nodes_commitment(exports.values().map(|e| &e.node));
408
409 Ok(Self { digest, exports, mast_forest })
410 }
411}
412
413impl Serializable for KernelLibrary {
415 fn write_into<W: ByteWriter>(&self, target: &mut W) {
416 let Self { kernel: _, kernel_info: _, library } = self;
417
418 library.write_into(target);
419 }
420}
421
422impl Deserializable for KernelLibrary {
424 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
425 let library = Library::read_from(source)?;
426
427 Self::try_from(library).map_err(|err| {
428 DeserializationError::InvalidValue(format!(
429 "Failed to deserialize kernel library: {err}"
430 ))
431 })
432 }
433}
434
435pub struct FunctionTypeSerializer<'a>(pub &'a FunctionType);
442
443impl Serializable for FunctionTypeSerializer<'_> {
444 fn write_into<W: ByteWriter>(&self, target: &mut W) {
445 target.write_u8(self.0.abi as u8);
446 target.write_usize(self.0.params().len());
447 target.write_many(self.0.params().iter().map(TypeSerializer));
448 target.write_usize(self.0.results().len());
449 target.write_many(self.0.results().iter().map(TypeSerializer));
450 }
451}
452
453pub struct FunctionTypeDeserializer(pub FunctionType);
460
461impl Deserializable for FunctionTypeDeserializer {
462 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
463 use midenc_hir_type::CallConv;
464
465 let abi = match source.read_u8()? {
466 0 => CallConv::Fast,
467 1 => CallConv::SystemV,
468 2 => CallConv::Wasm,
469 3 => CallConv::CanonLift,
470 4 => CallConv::CanonLower,
471 5 => CallConv::Kernel,
472 invalid => {
473 return Err(DeserializationError::InvalidValue(format!(
474 "invalid CallConv tag: {invalid}"
475 )));
476 },
477 };
478
479 let arity = source.read_usize()?;
480 let mut params = SmallVec::<[Type; 4]>::with_capacity(arity);
481 for _ in 0..arity {
482 let ty = TypeDeserializer::read_from(source)?.0;
483 params.push(ty);
484 }
485
486 let num_results = source.read_usize()?;
487 let mut results = SmallVec::<[Type; 1]>::with_capacity(num_results);
488 for _ in 0..num_results {
489 let ty = TypeDeserializer::read_from(source)?.0;
490 results.push(ty);
491 }
492
493 Ok(Self(FunctionType { abi, params, results }))
494 }
495}
496
497pub struct TypeSerializer<'a>(pub &'a Type);
503
504impl Serializable for TypeSerializer<'_> {
505 fn write_into<W: ByteWriter>(&self, target: &mut W) {
506 use midenc_hir_type::{AddressSpace, TypeRepr};
507
508 match self.0 {
509 Type::Unknown => target.write_u8(0),
510 Type::Never => target.write_u8(1),
511 Type::I1 => target.write_u8(2),
512 Type::I8 => target.write_u8(3),
513 Type::U8 => target.write_u8(4),
514 Type::I16 => target.write_u8(5),
515 Type::U16 => target.write_u8(6),
516 Type::I32 => target.write_u8(7),
517 Type::U32 => target.write_u8(8),
518 Type::I64 => target.write_u8(9),
519 Type::U64 => target.write_u8(10),
520 Type::I128 => target.write_u8(11),
521 Type::U128 => target.write_u8(12),
522 Type::U256 => target.write_u8(13),
523 Type::F64 => target.write_u8(14),
524 Type::Felt => target.write_u8(15),
525 Type::Ptr(ty) => {
526 target.write_u8(16);
527 match ty.addrspace {
528 AddressSpace::Byte => target.write_u8(0),
529 AddressSpace::Element => target.write_u8(1),
530 }
531 TypeSerializer(&ty.pointee).write_into(target);
532 },
533 Type::Struct(ty) => {
534 target.write_u8(17);
535 match ty.repr() {
536 TypeRepr::Default => target.write_u8(0),
537 TypeRepr::Align(align) => {
538 target.write_u8(1);
539 target.write_u16(align.get());
540 },
541 TypeRepr::Packed(align) => {
542 target.write_u8(2);
543 target.write_u16(align.get());
544 },
545 TypeRepr::Transparent => target.write_u8(3),
546 }
547 target.write_u8(ty.len() as u8);
548 for field in ty.fields() {
549 TypeSerializer(&field.ty).write_into(target);
550 }
551 },
552 Type::Array(ty) => {
553 target.write_u8(18);
554 target.write_usize(ty.len);
555 TypeSerializer(&ty.ty).write_into(target);
556 },
557 Type::List(ty) => {
558 target.write_u8(19);
559 TypeSerializer(ty).write_into(target);
560 },
561 Type::Function(ty) => {
562 target.write_u8(20);
563 FunctionTypeSerializer(ty).write_into(target);
564 },
565 }
566 }
567}
568
569pub struct TypeDeserializer(pub Type);
575
576impl Deserializable for TypeDeserializer {
577 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
578 use alloc::string::ToString;
579 use core::num::NonZeroU16;
580
581 use midenc_hir_type::{AddressSpace, ArrayType, PointerType, StructType, TypeRepr};
582
583 let ty = match source.read_u8()? {
584 0 => Type::Unknown,
585 1 => Type::Never,
586 2 => Type::I1,
587 3 => Type::I8,
588 4 => Type::U8,
589 5 => Type::I16,
590 6 => Type::U16,
591 7 => Type::I32,
592 8 => Type::U32,
593 9 => Type::I64,
594 10 => Type::U64,
595 11 => Type::I128,
596 12 => Type::U128,
597 13 => Type::U256,
598 14 => Type::F64,
599 15 => Type::Felt,
600 16 => {
601 let addrspace = match source.read_u8()? {
602 0 => AddressSpace::Byte,
603 1 => AddressSpace::Element,
604 invalid => {
605 return Err(DeserializationError::InvalidValue(format!(
606 "invalid AddressSpace tag: {invalid}"
607 )));
608 },
609 };
610 let pointee = TypeDeserializer::read_from(source)?.0;
611 Type::Ptr(Arc::new(PointerType { addrspace, pointee }))
612 },
613 17 => {
614 let repr = match source.read_u8()? {
615 0 => TypeRepr::Default,
616 1 => {
617 let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
618 DeserializationError::InvalidValue(
619 "invalid type repr: alignment must be a non-zero value".to_string(),
620 )
621 })?;
622 TypeRepr::Align(align)
623 },
624 2 => {
625 let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
626 DeserializationError::InvalidValue(
627 "invalid type repr: packed alignment must be a non-zero value"
628 .to_string(),
629 )
630 })?;
631 TypeRepr::Packed(align)
632 },
633 3 => TypeRepr::Transparent,
634 invalid => {
635 return Err(DeserializationError::InvalidValue(format!(
636 "invalid TypeRepr tag: {invalid}"
637 )));
638 },
639 };
640 let num_fields = source.read_u8()?;
641 let mut fields = SmallVec::<[Type; 4]>::with_capacity(num_fields as usize);
642 for _ in 0..num_fields {
643 let ty = TypeDeserializer::read_from(source)?.0;
644 fields.push(ty);
645 }
646 Type::Struct(Arc::new(StructType::new_with_repr(repr, fields)))
647 },
648 18 => {
649 let arity = source.read_usize()?;
650 let ty = TypeDeserializer::read_from(source)?.0;
651 Type::Array(Arc::new(ArrayType { ty, len: arity }))
652 },
653 19 => {
654 let ty = TypeDeserializer::read_from(source)?.0;
655 Type::List(Arc::new(ty))
656 },
657 20 => Type::Function(Arc::new(FunctionTypeDeserializer::read_from(source)?.0)),
658 invalid => {
659 return Err(DeserializationError::InvalidValue(format!(
660 "invalid Type tag: {invalid}"
661 )));
662 },
663 };
664 Ok(Self(ty))
665 }
666}