miden_assembly_syntax/library/
mod.rs

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