Skip to main content

miden_assembly_syntax/library/
mod.rs

1use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
2
3use miden_core::{
4    Word,
5    advice::AdviceMap,
6    mast::{MastForest, MastNodeExt, MastNodeId},
7    program::Kernel,
8    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
9};
10use midenc_hir_type::{FunctionType, Type};
11#[cfg(feature = "arbitrary")]
12use proptest::prelude::*;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15use smallvec::SmallVec;
16
17use crate::ast::{AttributeSet, Ident, Path, PathBuf, ProcedureName};
18
19mod error;
20mod module;
21
22pub use module::{ConstantInfo, ItemInfo, ModuleInfo, ProcedureInfo, TypeInfo};
23pub use semver::{Error as VersionError, Version};
24
25pub use self::error::LibraryError;
26
27// LIBRARY EXPORT
28// ================================================================================================
29
30/// Metadata about a procedure exported by the interface of a [Library]
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
34pub enum LibraryExport {
35    Procedure(ProcedureExport),
36    Constant(ConstantExport),
37    Type(TypeExport),
38}
39
40impl LibraryExport {
41    pub fn path(&self) -> Arc<Path> {
42        match self {
43            Self::Procedure(export) => export.path.clone(),
44            Self::Constant(export) => export.path.clone(),
45            Self::Type(export) => export.path.clone(),
46        }
47    }
48
49    pub fn as_procedure(&self) -> Option<&ProcedureExport> {
50        match self {
51            Self::Procedure(proc) => Some(proc),
52            Self::Constant(_) | Self::Type(_) => None,
53        }
54    }
55
56    pub fn unwrap_procedure(&self) -> &ProcedureExport {
57        match self {
58            Self::Procedure(proc) => proc,
59            Self::Constant(_) | Self::Type(_) => panic!("expected export to be a procedure"),
60        }
61    }
62}
63
64impl From<ProcedureExport> for LibraryExport {
65    fn from(value: ProcedureExport) -> Self {
66        Self::Procedure(value)
67    }
68}
69
70impl From<ConstantExport> for LibraryExport {
71    fn from(value: ConstantExport) -> Self {
72        Self::Constant(value)
73    }
74}
75
76impl From<TypeExport> for LibraryExport {
77    fn from(value: TypeExport) -> Self {
78        Self::Type(value)
79    }
80}
81
82#[cfg(feature = "arbitrary")]
83impl Arbitrary for LibraryExport {
84    type Parameters = ();
85
86    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
87        use proptest::{arbitrary::any, prop_oneof, strategy::Strategy};
88
89        prop_oneof![
90            any::<ProcedureExport>().prop_map(Self::Procedure),
91            any::<ConstantExport>().prop_map(Self::Constant),
92            any::<TypeExport>().prop_map(Self::Type),
93        ]
94        .boxed()
95    }
96
97    type Strategy = BoxedStrategy<Self>;
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
103pub struct ProcedureExport {
104    /// The id of the MAST root node of the exported procedure
105    pub node: MastNodeId,
106    /// The fully-qualified path of the exported procedure
107    #[cfg_attr(feature = "serde", serde(with = "crate::ast::path"))]
108    pub path: Arc<Path>,
109    /// The type signature of the exported procedure, if known
110    #[cfg_attr(feature = "serde", serde(default))]
111    pub signature: Option<FunctionType>,
112    #[cfg_attr(feature = "serde", serde(default))]
113    pub attributes: AttributeSet,
114}
115
116impl ProcedureExport {
117    /// Create a new [ProcedureExport] representing the export of `node` with `path`
118    pub fn new(node: MastNodeId, path: Arc<Path>) -> Self {
119        Self {
120            node,
121            path,
122            signature: None,
123            attributes: Default::default(),
124        }
125    }
126
127    /// Specify the type signature and ABI of this export
128    pub fn with_signature(mut self, signature: FunctionType) -> Self {
129        self.signature = Some(signature);
130        self
131    }
132
133    /// Specify the set of attributes attached to this export
134    pub fn with_attributes(mut self, attrs: AttributeSet) -> Self {
135        self.attributes = attrs;
136        self
137    }
138}
139
140#[cfg(feature = "arbitrary")]
141impl Arbitrary for ProcedureExport {
142    type Parameters = ();
143
144    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
145        use proptest::collection::vec as prop_vec;
146
147        // Generate a small set of simple types for params/results to keep strategies fast/stable
148        let simple_type = prop_oneof![Just(Type::Felt), Just(Type::U32), Just(Type::U64),];
149
150        // Small vectors of params/results
151        let params = prop_vec(simple_type.clone(), 0..=4);
152        let results = prop_vec(simple_type, 0..=2);
153
154        // Use Fast ABI for roundtrip coverage
155        let abi = Just(midenc_hir_type::CallConv::Fast);
156
157        // Option<FunctionType>
158        let signature =
159            prop::option::of((abi, params, results).prop_map(|(abi, params_vec, results_vec)| {
160                let params = SmallVec::<[Type; 4]>::from_vec(params_vec);
161                let results = SmallVec::<[Type; 1]>::from_vec(results_vec);
162                FunctionType { abi, params, results }
163            }));
164
165        let nid = any::<MastNodeId>();
166        let name = any::<crate::ast::QualifiedProcedureName>();
167        (nid, name, signature)
168            .prop_map(|(nodeid, procname, signature)| Self {
169                node: nodeid,
170                path: procname.to_path_buf().into(),
171                signature,
172                attributes: Default::default(),
173            })
174            .boxed()
175    }
176
177    type Strategy = BoxedStrategy<Self>;
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
182#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
183#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
184pub struct ConstantExport {
185    /// The fully-qualified path of the exported constant
186    #[cfg_attr(feature = "serde", serde(with = "crate::ast::path"))]
187    #[cfg_attr(
188        feature = "arbitrary",
189        proptest(strategy = "crate::arbitrary::path::constant_path_random_length(1)")
190    )]
191    pub path: Arc<Path>,
192    /// The constant-folded AST representing the value of this constant
193    pub value: crate::ast::ConstantValue,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
199pub struct TypeExport {
200    /// The fully-qualified path of the exported type declaration
201    #[cfg_attr(feature = "serde", serde(with = "crate::ast::path"))]
202    pub path: Arc<Path>,
203    /// The type bound to `name`
204    pub ty: crate::ast::types::Type,
205}
206
207#[cfg(feature = "arbitrary")]
208impl Arbitrary for TypeExport {
209    type Parameters = ();
210
211    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
212        use proptest::strategy::{Just, Strategy};
213        let path = crate::arbitrary::path::user_defined_type_path_random_length(1);
214        let ty = Just(crate::ast::types::Type::Felt);
215
216        (path, ty).prop_map(|(path, ty)| Self { path, ty }).boxed()
217    }
218
219    type Strategy = BoxedStrategy<Self>;
220}
221
222// LIBRARY
223// ================================================================================================
224
225/// Represents a library where all modules were compiled into a [`MastForest`].
226///
227/// A library exports a set of one or more procedures. Currently, all exported procedures belong
228/// to the same top-level namespace.
229#[derive(Debug, Clone, PartialEq, Eq)]
230#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
231pub struct Library {
232    /// The content hash of this library, formed by hashing the roots of all exports in
233    /// lexicographical order (by digest, not procedure name)
234    digest: Word,
235    /// A map between procedure paths and the corresponding procedure metadata in the MAST forest.
236    /// Multiple paths can map to the same root, and also, some roots may not be associated with
237    /// any paths.
238    ///
239    /// Note that we use `MastNodeId` as an identifier for procedures instead of MAST root, since 2
240    /// different procedures with the same MAST root can be different due to the decorators they
241    /// contain. However, note that `MastNodeId` is also not a unique identifier for procedures; if
242    /// the procedures have the same MAST root and decorators, they will have the same
243    /// `MastNodeId`.
244    exports: BTreeMap<Arc<Path>, LibraryExport>,
245    /// The MAST forest underlying this library.
246    mast_forest: Arc<MastForest>,
247}
248
249impl AsRef<Library> for Library {
250    #[inline(always)]
251    fn as_ref(&self) -> &Library {
252        self
253    }
254}
255
256// ------------------------------------------------------------------------------------------------
257/// Constructors
258impl Library {
259    /// Constructs a new [`Library`] from the provided MAST forest and a set of exports.
260    ///
261    /// # Errors
262    /// Returns an error if the set of exports is empty.
263    /// Returns an error if any of the specified exports do not have a corresponding procedure root
264    /// in the provided MAST forest.
265    pub fn new(
266        mast_forest: Arc<MastForest>,
267        exports: BTreeMap<Arc<Path>, LibraryExport>,
268    ) -> Result<Self, LibraryError> {
269        if exports.is_empty() {
270            return Err(LibraryError::NoExport);
271        }
272
273        for export in exports.values() {
274            if let LibraryExport::Procedure(ProcedureExport { node, path, .. }) = export
275                && !mast_forest.is_procedure_root(*node)
276            {
277                return Err(LibraryError::NoProcedureRootForExport {
278                    procedure_path: path.clone(),
279                });
280            }
281        }
282
283        let digest =
284            mast_forest.compute_nodes_commitment(exports.values().filter_map(
285                |export| match export {
286                    LibraryExport::Procedure(export) => Some(&export.node),
287                    LibraryExport::Constant(_) | LibraryExport::Type(_) => None,
288                },
289            ));
290
291        Ok(Self { digest, exports, mast_forest })
292    }
293
294    /// Produces a new library with the existing [`MastForest`] and where all key/values in the
295    /// provided advice map are added to the internal advice map.
296    pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
297        let mut mast_forest = (*self.mast_forest).clone();
298        mast_forest.advice_map_mut().extend(advice_map);
299        Self {
300            mast_forest: Arc::new(mast_forest),
301            ..self
302        }
303    }
304}
305
306// ------------------------------------------------------------------------------------------------
307/// Public accessors
308impl Library {
309    /// Returns the [Word] representing the content hash of this library
310    pub fn digest(&self) -> &Word {
311        &self.digest
312    }
313
314    /// Returns the fully qualified name and metadata of all procedures exported by the library.
315    pub fn exports(&self) -> impl Iterator<Item = &LibraryExport> {
316        self.exports.values()
317    }
318
319    /// Returns the number of exports in this library.
320    pub fn num_exports(&self) -> usize {
321        self.exports.len()
322    }
323
324    /// Returns a MAST node ID associated with the specified exported procedure.
325    ///
326    /// # Panics
327    /// Panics if the specified procedure is not exported from this library.
328    pub fn get_export_node_id(&self, path: impl AsRef<Path>) -> MastNodeId {
329        let path = path.as_ref().to_absolute();
330        self.exports
331            .get(path.as_ref())
332            .expect("procedure not exported from the library")
333            .unwrap_procedure()
334            .node
335    }
336
337    /// Returns true if the specified exported procedure is re-exported from a dependency.
338    pub fn is_reexport(&self, path: impl AsRef<Path>) -> bool {
339        let path = path.as_ref().to_absolute();
340        self.exports
341            .get(path.as_ref())
342            .and_then(LibraryExport::as_procedure)
343            .map(|export| self.mast_forest[export.node].is_external())
344            .unwrap_or(false)
345    }
346
347    /// Returns a reference to the inner [`MastForest`].
348    pub fn mast_forest(&self) -> &Arc<MastForest> {
349        &self.mast_forest
350    }
351
352    /// Returns the digest of the procedure with the specified name, or `None` if it was not found
353    /// in the library or its library path is malformed.
354    pub fn get_procedure_root_by_path(&self, path: impl AsRef<Path>) -> Option<Word> {
355        let path = path.as_ref().to_absolute();
356        let export = self.exports.get(path.as_ref()).and_then(LibraryExport::as_procedure);
357        export.map(|e| self.mast_forest()[e.node].digest())
358    }
359}
360
361/// Conversions
362impl Library {
363    /// Returns an iterator over the module infos of the library.
364    pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
365        let mut modules_by_path: BTreeMap<Arc<Path>, ModuleInfo> = BTreeMap::new();
366
367        for export in self.exports.values() {
368            let module_name =
369                Arc::from(export.path().parent().unwrap().to_path_buf().into_boxed_path());
370            let module = modules_by_path
371                .entry(Arc::clone(&module_name))
372                .or_insert_with(|| ModuleInfo::new(module_name));
373            match export {
374                LibraryExport::Procedure(ProcedureExport { node, path, signature, attributes }) => {
375                    let proc_digest = self.mast_forest[*node].digest();
376                    let name = path.last().unwrap();
377                    module.add_procedure(
378                        ProcedureName::new(name).expect("valid procedure name"),
379                        proc_digest,
380                        signature.clone().map(Arc::new),
381                        attributes.clone(),
382                    );
383                },
384                LibraryExport::Constant(ConstantExport { path, value }) => {
385                    let name = Ident::new(path.last().unwrap()).expect("valid identifier");
386                    module.add_constant(name, value.clone());
387                },
388                LibraryExport::Type(TypeExport { path, ty }) => {
389                    let name = Ident::new(path.last().unwrap()).expect("valid identifier");
390                    module.add_type(name, ty.clone());
391                },
392            }
393        }
394
395        modules_by_path.into_values()
396    }
397}
398
399#[cfg(feature = "std")]
400impl Library {
401    /// File extension for the Assembly Library.
402    pub const LIBRARY_EXTENSION: &'static str = "masl";
403
404    /// Write the library to a target file
405    ///
406    /// NOTE: It is up to the caller to use the correct file extension, but there is no
407    /// specific requirement that the extension be set, or the same as
408    /// [`Self::LIBRARY_EXTENSION`].
409    pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
410        let path = path.as_ref();
411
412        if let Some(dir) = path.parent() {
413            std::fs::create_dir_all(dir)?;
414        }
415
416        // NOTE: We catch panics due to i/o errors here due to the fact that the ByteWriter
417        // trait does not provide fallible APIs, so WriteAdapter will panic if the underlying
418        // writes fail. This needs to be addressed upstream at some point
419        std::panic::catch_unwind(|| {
420            let mut file = std::fs::File::create(path)?;
421            self.write_into(&mut file);
422            Ok(())
423        })
424        .map_err(|p| {
425            match p.downcast::<std::io::Error>() {
426                // SAFETY: It is guaranteed safe to read Box<std::io::Error>
427                Ok(err) => unsafe { core::ptr::read(&*err) },
428                Err(err) => std::panic::resume_unwind(err),
429            }
430        })?
431    }
432
433    pub fn deserialize_from_file(
434        path: impl AsRef<std::path::Path>,
435    ) -> Result<Self, DeserializationError> {
436        use miden_core::utils::ReadAdapter;
437
438        let path = path.as_ref();
439        let mut file = std::fs::File::open(path).map_err(|err| {
440            DeserializationError::InvalidValue(format!(
441                "failed to open file at {}: {err}",
442                path.to_string_lossy()
443            ))
444        })?;
445        let mut adapter = ReadAdapter::new(&mut file);
446
447        Self::read_from(&mut adapter)
448    }
449}
450
451// KERNEL LIBRARY
452// ================================================================================================
453
454/// Represents a library containing a Miden VM kernel.
455///
456/// This differs from the regular [Library] as follows:
457/// - All exported procedures must be exported directly from the kernel namespace (i.e., `$kernel`).
458/// - There must be at least one exported procedure.
459/// - The number of exported procedures cannot exceed [Kernel::MAX_NUM_PROCEDURES] (i.e., 256).
460#[derive(Debug, Clone, PartialEq, Eq)]
461#[cfg_attr(feature = "serde", derive(Deserialize))]
462#[cfg_attr(feature = "serde", serde(try_from = "Library"))]
463pub struct KernelLibrary {
464    #[cfg_attr(feature = "serde", serde(skip))]
465    kernel: Kernel,
466    #[cfg_attr(feature = "serde", serde(skip))]
467    kernel_info: ModuleInfo,
468    library: Library,
469}
470
471#[cfg(feature = "serde")]
472impl serde::Serialize for KernelLibrary {
473    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
474    where
475        S: serde::Serializer,
476    {
477        Library::serialize(&self.library, serializer)
478    }
479}
480
481impl AsRef<Library> for KernelLibrary {
482    #[inline(always)]
483    fn as_ref(&self) -> &Library {
484        &self.library
485    }
486}
487
488impl KernelLibrary {
489    /// Returns the [Kernel] for this kernel library.
490    pub fn kernel(&self) -> &Kernel {
491        &self.kernel
492    }
493
494    /// Returns a reference to the inner [`MastForest`].
495    pub fn mast_forest(&self) -> &Arc<MastForest> {
496        self.library.mast_forest()
497    }
498
499    /// Destructures this kernel library into individual parts.
500    pub fn into_parts(self) -> (Kernel, ModuleInfo, Arc<MastForest>) {
501        (self.kernel, self.kernel_info, self.library.mast_forest)
502    }
503}
504
505impl TryFrom<Library> for KernelLibrary {
506    type Error = LibraryError;
507
508    fn try_from(library: Library) -> Result<Self, Self::Error> {
509        let kernel_path = Arc::from(Path::kernel_path().to_path_buf().into_boxed_path());
510        let mut proc_digests = Vec::with_capacity(library.exports.len());
511
512        let mut kernel_module = ModuleInfo::new(Arc::clone(&kernel_path));
513
514        for export in library.exports.values() {
515            match export {
516                LibraryExport::Procedure(export) => {
517                    // make sure all procedures are exported only from the kernel root
518                    if !export.path.is_in_kernel() {
519                        return Err(LibraryError::InvalidKernelExport {
520                            procedure_path: export.path.clone(),
521                        });
522                    }
523
524                    let proc_digest = library.mast_forest[export.node].digest();
525                    proc_digests.push(proc_digest);
526                    kernel_module.add_procedure(
527                        ProcedureName::new(export.path.last().unwrap())
528                            .expect("valid procedure name"),
529                        proc_digest,
530                        export.signature.clone().map(Arc::new),
531                        export.attributes.clone(),
532                    );
533                },
534                LibraryExport::Constant(export) => {
535                    // Only export constants from the kernel root
536                    if export.path.is_in_kernel() {
537                        let name =
538                            Ident::new(export.path.last().unwrap()).expect("valid identifier");
539                        kernel_module.add_constant(name, export.value.clone());
540                    }
541                },
542                LibraryExport::Type(export) => {
543                    // Only export types from the kernel root
544                    if export.path.is_in_kernel() {
545                        let name =
546                            Ident::new(export.path.last().unwrap()).expect("valid identifier");
547                        kernel_module.add_type(name, export.ty.clone());
548                    }
549                },
550            }
551        }
552
553        let kernel = Kernel::new(&proc_digests).map_err(LibraryError::KernelConversion)?;
554
555        Ok(Self {
556            kernel,
557            kernel_info: kernel_module,
558            library,
559        })
560    }
561}
562
563#[cfg(feature = "std")]
564impl KernelLibrary {
565    /// Write the library to a target file
566    pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
567        self.library.write_to_file(path)
568    }
569}
570
571// LIBRARY SERIALIZATION
572// ================================================================================================
573
574/// NOTE: Serialization of libraries is likely to be deprecated in a future release
575impl Serializable for Library {
576    fn write_into<W: ByteWriter>(&self, target: &mut W) {
577        let Self { digest: _, exports, mast_forest } = self;
578
579        mast_forest.write_into(target);
580
581        target.write_usize(exports.len());
582        for export in exports.values() {
583            export.write_into(target);
584        }
585    }
586}
587
588/// NOTE: Serialization of libraries is likely to be deprecated in a future release
589impl Deserializable for Library {
590    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
591        let mast_forest = Arc::new(MastForest::read_from(source)?);
592
593        let num_exports = source.read_usize()?;
594        if num_exports == 0 {
595            return Err(DeserializationError::InvalidValue(String::from("No exported procedures")));
596        };
597        let mut exports = BTreeMap::new();
598        for _ in 0..num_exports {
599            let tag = source.read_u8()?;
600            let path: PathBuf = source.read()?;
601            let path = Arc::<Path>::from(path.into_boxed_path());
602            let export = match tag {
603                0 => {
604                    let node = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
605                    let signature = if source.read_bool()? {
606                        Some(FunctionTypeDeserializer::read_from(source)?.0)
607                    } else {
608                        None
609                    };
610                    let attributes = AttributeSet::read_from(source)?;
611                    LibraryExport::Procedure(ProcedureExport {
612                        node,
613                        path: path.clone(),
614                        signature,
615                        attributes,
616                    })
617                },
618                1 => {
619                    let value = crate::ast::ConstantValue::read_from(source)?;
620                    LibraryExport::Constant(ConstantExport { path: path.clone(), value })
621                },
622                2 => {
623                    let ty = TypeDeserializer::read_from(source)?.0;
624                    LibraryExport::Type(TypeExport { path: path.clone(), ty })
625                },
626                invalid => {
627                    return Err(DeserializationError::InvalidValue(format!(
628                        "unknown LibraryExport tag: '{invalid}'"
629                    )));
630                },
631            };
632            exports.insert(path, export);
633        }
634
635        let digest =
636            mast_forest.compute_nodes_commitment(exports.values().filter_map(|e| match e {
637                LibraryExport::Procedure(e) => Some(&e.node),
638                LibraryExport::Constant(_) | LibraryExport::Type(_) => None,
639            }));
640
641        Ok(Self { digest, exports, mast_forest })
642    }
643}
644
645#[cfg(feature = "serde")]
646impl serde::Serialize for Library {
647    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
648    where
649        S: serde::Serializer,
650    {
651        use serde::ser::SerializeStruct;
652
653        struct LibraryExports<'a>(&'a BTreeMap<Arc<Path>, LibraryExport>);
654        impl serde::Serialize for LibraryExports<'_> {
655            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
656            where
657                S: serde::Serializer,
658            {
659                use serde::ser::SerializeSeq;
660                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
661                for elem in self.0.values() {
662                    serializer.serialize_element(elem)?;
663                }
664                serializer.end()
665            }
666        }
667
668        let Self { digest: _, exports, mast_forest } = self;
669
670        let mut serializer = serializer.serialize_struct("Library", 2)?;
671        serializer.serialize_field("mast_forest", mast_forest)?;
672        serializer.serialize_field("exports", &LibraryExports(exports))?;
673        serializer.end()
674    }
675}
676
677#[cfg(feature = "serde")]
678impl<'de> serde::Deserialize<'de> for Library {
679    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
680    where
681        D: serde::Deserializer<'de>,
682    {
683        use serde::de::Visitor;
684
685        #[derive(Deserialize)]
686        #[serde(field_identifier, rename_all = "snake_case")]
687        enum Field {
688            MastForest,
689            Exports,
690        }
691
692        struct LibraryVisitor;
693
694        impl<'de> Visitor<'de> for LibraryVisitor {
695            type Value = Library;
696
697            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
698                formatter.write_str("struct Library")
699            }
700
701            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
702            where
703                A: serde::de::SeqAccess<'de>,
704            {
705                let mast_forest = seq
706                    .next_element()?
707                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
708                let exports: Vec<LibraryExport> = seq
709                    .next_element()?
710                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
711                let exports = exports.into_iter().map(|export| (export.path(), export)).collect();
712                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
713            }
714
715            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
716            where
717                A: serde::de::MapAccess<'de>,
718            {
719                let mut mast_forest = None;
720                let mut exports = None;
721                while let Some(key) = map.next_key()? {
722                    match key {
723                        Field::MastForest => {
724                            if mast_forest.is_some() {
725                                return Err(serde::de::Error::duplicate_field("mast_forest"));
726                            }
727                            mast_forest = Some(map.next_value()?);
728                        },
729                        Field::Exports => {
730                            if exports.is_some() {
731                                return Err(serde::de::Error::duplicate_field("exports"));
732                            }
733                            let items: Vec<LibraryExport> = map.next_value()?;
734                            exports = Some(
735                                items.into_iter().map(|export| (export.path(), export)).collect(),
736                            );
737                        },
738                    }
739                }
740                let mast_forest =
741                    mast_forest.ok_or_else(|| serde::de::Error::missing_field("mast_forest"))?;
742                let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
743                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
744            }
745        }
746
747        deserializer.deserialize_struct("Library", &["mast_forest", "exports"], LibraryVisitor)
748    }
749}
750
751/// NOTE: Serialization of libraries is likely to be deprecated in a future release
752impl Serializable for KernelLibrary {
753    fn write_into<W: ByteWriter>(&self, target: &mut W) {
754        let Self { kernel: _, kernel_info: _, library } = self;
755
756        library.write_into(target);
757    }
758}
759
760/// NOTE: Serialization of libraries is likely to be deprecated in a future release
761impl Deserializable for KernelLibrary {
762    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
763        let library = Library::read_from(source)?;
764
765        Self::try_from(library).map_err(|err| {
766            DeserializationError::InvalidValue(format!(
767                "Failed to deserialize kernel library: {err}"
768            ))
769        })
770    }
771}
772
773/// NOTE: Deserialization is handled in the implementation for [Library]
774impl Serializable for LibraryExport {
775    fn write_into<W: ByteWriter>(&self, target: &mut W) {
776        match self {
777            LibraryExport::Procedure(ProcedureExport {
778                node,
779                path: name,
780                signature,
781                attributes,
782            }) => {
783                target.write_u8(0);
784                name.write_into(target);
785                target.write_u32(u32::from(*node));
786                if let Some(sig) = signature {
787                    target.write_bool(true);
788                    FunctionTypeSerializer(sig).write_into(target);
789                } else {
790                    target.write_bool(false);
791                }
792                attributes.write_into(target);
793            },
794            LibraryExport::Constant(ConstantExport { path: name, value }) => {
795                target.write_u8(1);
796                name.write_into(target);
797                value.write_into(target);
798            },
799            LibraryExport::Type(TypeExport { path: name, ty }) => {
800                target.write_u8(2);
801                name.write_into(target);
802                TypeSerializer(ty).write_into(target);
803            },
804        }
805    }
806}
807
808/// A wrapper type for [FunctionType] that provides serialization support via the miden-crypto
809/// serializer.
810///
811/// This is a temporary implementation to allow type information to be serialized with libraries,
812/// but in a future release we'll either rely on the `serde` serialization for these types, or
813/// provide the serialization implementation in midenc-hir-type instead
814pub struct FunctionTypeSerializer<'a>(pub &'a FunctionType);
815
816impl Serializable for FunctionTypeSerializer<'_> {
817    fn write_into<W: ByteWriter>(&self, target: &mut W) {
818        target.write_u8(self.0.abi as u8);
819        target.write_usize(self.0.params().len());
820        target.write_many(self.0.params().iter().map(TypeSerializer));
821        target.write_usize(self.0.results().len());
822        target.write_many(self.0.results().iter().map(TypeSerializer));
823    }
824}
825
826/// A wrapper type for [FunctionType] that provides deserialization support via the miden-crypto
827/// serializer.
828///
829/// This is a temporary implementation to allow type information to be serialized with libraries,
830/// but in a future release we'll either rely on the `serde` serialization for these types, or
831/// provide the serialization implementation in midenc-hir-type instead
832pub struct FunctionTypeDeserializer(pub FunctionType);
833
834impl Deserializable for FunctionTypeDeserializer {
835    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
836        use midenc_hir_type::CallConv;
837
838        let abi = match source.read_u8()? {
839            0 => CallConv::Fast,
840            1 => CallConv::SystemV,
841            2 => CallConv::Wasm,
842            3 => CallConv::CanonLift,
843            4 => CallConv::CanonLower,
844            5 => CallConv::Kernel,
845            invalid => {
846                return Err(DeserializationError::InvalidValue(format!(
847                    "invalid CallConv tag: {invalid}"
848                )));
849            },
850        };
851
852        let arity = source.read_usize()?;
853        let mut params = SmallVec::<[Type; 4]>::with_capacity(arity);
854        for _ in 0..arity {
855            let ty = TypeDeserializer::read_from(source)?.0;
856            params.push(ty);
857        }
858
859        let num_results = source.read_usize()?;
860        let mut results = SmallVec::<[Type; 1]>::with_capacity(num_results);
861        for _ in 0..num_results {
862            let ty = TypeDeserializer::read_from(source)?.0;
863            results.push(ty);
864        }
865
866        Ok(Self(FunctionType { abi, params, results }))
867    }
868}
869
870/// A wrapper type for [Type] that provides serialization support via the miden-crypto serializer.
871///
872/// This is a temporary implementation to allow type information to be serialized with libraries,
873/// but in a future release we'll either rely on the `serde` serialization for these types, or
874/// provide the serialization implementation in midenc-hir-type instead
875pub struct TypeSerializer<'a>(pub &'a Type);
876
877impl Serializable for TypeSerializer<'_> {
878    fn write_into<W: ByteWriter>(&self, target: &mut W) {
879        use midenc_hir_type::{AddressSpace, TypeRepr};
880
881        match self.0 {
882            Type::Unknown => target.write_u8(0),
883            Type::Never => target.write_u8(1),
884            Type::I1 => target.write_u8(2),
885            Type::I8 => target.write_u8(3),
886            Type::U8 => target.write_u8(4),
887            Type::I16 => target.write_u8(5),
888            Type::U16 => target.write_u8(6),
889            Type::I32 => target.write_u8(7),
890            Type::U32 => target.write_u8(8),
891            Type::I64 => target.write_u8(9),
892            Type::U64 => target.write_u8(10),
893            Type::I128 => target.write_u8(11),
894            Type::U128 => target.write_u8(12),
895            Type::U256 => target.write_u8(13),
896            Type::F64 => target.write_u8(14),
897            Type::Felt => target.write_u8(15),
898            Type::Ptr(ty) => {
899                target.write_u8(16);
900                match ty.addrspace {
901                    AddressSpace::Byte => target.write_u8(0),
902                    AddressSpace::Element => target.write_u8(1),
903                }
904                TypeSerializer(&ty.pointee).write_into(target);
905            },
906            Type::Struct(ty) => {
907                target.write_u8(17);
908                match ty.repr() {
909                    TypeRepr::Default => target.write_u8(0),
910                    TypeRepr::Align(align) => {
911                        target.write_u8(1);
912                        target.write_u16(align.get());
913                    },
914                    TypeRepr::Packed(align) => {
915                        target.write_u8(2);
916                        target.write_u16(align.get());
917                    },
918                    TypeRepr::Transparent => target.write_u8(3),
919                    TypeRepr::BigEndian => target.write_u8(4),
920                }
921                target.write_u8(ty.len() as u8);
922                for field in ty.fields() {
923                    TypeSerializer(&field.ty).write_into(target);
924                }
925            },
926            Type::Array(ty) => {
927                target.write_u8(18);
928                target.write_usize(ty.len);
929                TypeSerializer(&ty.ty).write_into(target);
930            },
931            Type::List(ty) => {
932                target.write_u8(19);
933                TypeSerializer(ty).write_into(target);
934            },
935            Type::Function(ty) => {
936                target.write_u8(20);
937                FunctionTypeSerializer(ty).write_into(target);
938            },
939        }
940    }
941}
942
943/// A wrapper type for [Type] that provides deserialization support via the miden-crypto serializer.
944///
945/// This is a temporary implementation to allow type information to be serialized with libraries,
946/// but in a future release we'll either rely on the `serde` serialization for these types, or
947/// provide the serialization implementation in midenc-hir-type instead
948pub struct TypeDeserializer(pub Type);
949
950impl Deserializable for TypeDeserializer {
951    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
952        use alloc::string::ToString;
953        use core::num::NonZeroU16;
954
955        use midenc_hir_type::{AddressSpace, ArrayType, PointerType, StructType, TypeRepr};
956
957        let ty = match source.read_u8()? {
958            0 => Type::Unknown,
959            1 => Type::Never,
960            2 => Type::I1,
961            3 => Type::I8,
962            4 => Type::U8,
963            5 => Type::I16,
964            6 => Type::U16,
965            7 => Type::I32,
966            8 => Type::U32,
967            9 => Type::I64,
968            10 => Type::U64,
969            11 => Type::I128,
970            12 => Type::U128,
971            13 => Type::U256,
972            14 => Type::F64,
973            15 => Type::Felt,
974            16 => {
975                let addrspace = match source.read_u8()? {
976                    0 => AddressSpace::Byte,
977                    1 => AddressSpace::Element,
978                    invalid => {
979                        return Err(DeserializationError::InvalidValue(format!(
980                            "invalid AddressSpace tag: {invalid}"
981                        )));
982                    },
983                };
984                let pointee = TypeDeserializer::read_from(source)?.0;
985                Type::Ptr(Arc::new(PointerType { addrspace, pointee }))
986            },
987            17 => {
988                let repr = match source.read_u8()? {
989                    0 => TypeRepr::Default,
990                    1 => {
991                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
992                            DeserializationError::InvalidValue(
993                                "invalid type repr: alignment must be a non-zero value".to_string(),
994                            )
995                        })?;
996                        TypeRepr::Align(align)
997                    },
998                    2 => {
999                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
1000                            DeserializationError::InvalidValue(
1001                                "invalid type repr: packed alignment must be a non-zero value"
1002                                    .to_string(),
1003                            )
1004                        })?;
1005                        TypeRepr::Packed(align)
1006                    },
1007                    3 => TypeRepr::Transparent,
1008                    invalid => {
1009                        return Err(DeserializationError::InvalidValue(format!(
1010                            "invalid TypeRepr tag: {invalid}"
1011                        )));
1012                    },
1013                };
1014                let num_fields = source.read_u8()?;
1015                let mut fields = SmallVec::<[Type; 4]>::with_capacity(num_fields as usize);
1016                for _ in 0..num_fields {
1017                    let ty = TypeDeserializer::read_from(source)?.0;
1018                    fields.push(ty);
1019                }
1020                Type::Struct(Arc::new(StructType::new_with_repr(repr, fields)))
1021            },
1022            18 => {
1023                let arity = source.read_usize()?;
1024                let ty = TypeDeserializer::read_from(source)?.0;
1025                Type::Array(Arc::new(ArrayType { ty, len: arity }))
1026            },
1027            19 => {
1028                let ty = TypeDeserializer::read_from(source)?.0;
1029                Type::List(Arc::new(ty))
1030            },
1031            20 => Type::Function(Arc::new(FunctionTypeDeserializer::read_from(source)?.0)),
1032            invalid => {
1033                return Err(DeserializationError::InvalidValue(format!(
1034                    "invalid Type tag: {invalid}"
1035                )));
1036            },
1037        };
1038        Ok(Self(ty))
1039    }
1040}
1041
1042#[cfg(feature = "arbitrary")]
1043impl proptest::prelude::Arbitrary for Library {
1044    type Parameters = ();
1045
1046    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1047        use miden_core::{
1048            mast::{BasicBlockNodeBuilder, MastForestContributor},
1049            operations::Operation,
1050        };
1051        use proptest::prelude::*;
1052
1053        prop::collection::vec(any::<LibraryExport>(), 1..5)
1054            .prop_map(|exports| {
1055                let mut exports =
1056                    BTreeMap::from_iter(exports.into_iter().map(|export| (export.path(), export)));
1057                // Create a MastForest with actual nodes for the exports
1058                let mut mast_forest = MastForest::new();
1059                let mut nodes = Vec::new();
1060
1061                for export in exports.values() {
1062                    if let LibraryExport::Procedure(export) = export {
1063                        let node_id = BasicBlockNodeBuilder::new(
1064                            vec![Operation::Add, Operation::Mul],
1065                            Vec::new(),
1066                        )
1067                        .add_to_forest(&mut mast_forest)
1068                        .unwrap();
1069                        nodes.push((export.node, node_id));
1070                    }
1071                }
1072
1073                // Replace the export node IDs with the actual node IDs we created
1074                let mut procedure_exports = 0;
1075                for export in exports.values_mut() {
1076                    match export {
1077                        LibraryExport::Procedure(export) => {
1078                            procedure_exports += 1;
1079                            // Find the corresponding node we created
1080                            if let Some(&(_, actual_node_id)) =
1081                                nodes.iter().find(|(original_id, _)| *original_id == export.node)
1082                            {
1083                                export.node = actual_node_id;
1084                            } else {
1085                                // If we can't find the node (shouldn't happen), use the first node
1086                                // we created
1087                                if let Some(&(_, first_node_id)) = nodes.first() {
1088                                    export.node = first_node_id;
1089                                } else {
1090                                    // This should never happen since we create nodes for each
1091                                    // export
1092                                    panic!("No nodes created for exports");
1093                                }
1094                            }
1095                        },
1096                        LibraryExport::Constant(_) | LibraryExport::Type(_) => (),
1097                    }
1098                }
1099
1100                let mut node_ids = Vec::with_capacity(procedure_exports);
1101                for export in exports.values() {
1102                    if let LibraryExport::Procedure(export) = export {
1103                        // Add the node to the forest roots if it's not already there
1104                        mast_forest.make_root(export.node);
1105                        // Collect the node id for recomputing the digest
1106                        node_ids.push(export.node);
1107                    }
1108                }
1109
1110                // Recompute the digest
1111                let digest = mast_forest.compute_nodes_commitment(&node_ids);
1112
1113                let mast_forest = Arc::new(mast_forest);
1114                Library { digest, exports, mast_forest }
1115            })
1116            .boxed()
1117    }
1118
1119    type Strategy = proptest::prelude::BoxedStrategy<Self>;
1120}