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    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                    let attributes = AttributeSet::read_from(source)?;
609                    LibraryExport::Procedure(ProcedureExport {
610                        node,
611                        path: path.clone(),
612                        signature,
613                        attributes,
614                    })
615                },
616                1 => {
617                    let value = crate::ast::ConstantValue::read_from(source)?;
618                    LibraryExport::Constant(ConstantExport { path: path.clone(), value })
619                },
620                2 => {
621                    let ty = TypeDeserializer::read_from(source)?.0;
622                    LibraryExport::Type(TypeExport { path: path.clone(), ty })
623                },
624                invalid => {
625                    return Err(DeserializationError::InvalidValue(format!(
626                        "unknown LibraryExport tag: '{invalid}'"
627                    )));
628                },
629            };
630            exports.insert(path, export);
631        }
632
633        let digest =
634            mast_forest.compute_nodes_commitment(exports.values().filter_map(|e| match e {
635                LibraryExport::Procedure(e) => Some(&e.node),
636                LibraryExport::Constant(_) | LibraryExport::Type(_) => None,
637            }));
638
639        Ok(Self { digest, exports, mast_forest })
640    }
641}
642
643#[cfg(feature = "serde")]
644impl serde::Serialize for Library {
645    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
646    where
647        S: serde::Serializer,
648    {
649        use serde::ser::SerializeStruct;
650
651        struct LibraryExports<'a>(&'a BTreeMap<Arc<Path>, LibraryExport>);
652        impl serde::Serialize for LibraryExports<'_> {
653            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
654            where
655                S: serde::Serializer,
656            {
657                use serde::ser::SerializeSeq;
658                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
659                for elem in self.0.values() {
660                    serializer.serialize_element(elem)?;
661                }
662                serializer.end()
663            }
664        }
665
666        let Self { digest: _, exports, mast_forest } = self;
667
668        let mut serializer = serializer.serialize_struct("Library", 2)?;
669        serializer.serialize_field("mast_forest", mast_forest)?;
670        serializer.serialize_field("exports", &LibraryExports(exports))?;
671        serializer.end()
672    }
673}
674
675#[cfg(feature = "serde")]
676impl<'de> serde::Deserialize<'de> for Library {
677    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
678    where
679        D: serde::Deserializer<'de>,
680    {
681        use serde::de::Visitor;
682
683        #[derive(Deserialize)]
684        #[serde(field_identifier, rename_all = "snake_case")]
685        enum Field {
686            MastForest,
687            Exports,
688        }
689
690        struct LibraryVisitor;
691
692        impl<'de> Visitor<'de> for LibraryVisitor {
693            type Value = Library;
694
695            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
696                formatter.write_str("struct Library")
697            }
698
699            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
700            where
701                A: serde::de::SeqAccess<'de>,
702            {
703                let mast_forest = seq
704                    .next_element()?
705                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
706                let exports: Vec<LibraryExport> = seq
707                    .next_element()?
708                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
709                let exports = exports.into_iter().map(|export| (export.path(), export)).collect();
710                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
711            }
712
713            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
714            where
715                A: serde::de::MapAccess<'de>,
716            {
717                let mut mast_forest = None;
718                let mut exports = None;
719                while let Some(key) = map.next_key()? {
720                    match key {
721                        Field::MastForest => {
722                            if mast_forest.is_some() {
723                                return Err(serde::de::Error::duplicate_field("mast_forest"));
724                            }
725                            mast_forest = Some(map.next_value()?);
726                        },
727                        Field::Exports => {
728                            if exports.is_some() {
729                                return Err(serde::de::Error::duplicate_field("exports"));
730                            }
731                            let items: Vec<LibraryExport> = map.next_value()?;
732                            exports = Some(
733                                items.into_iter().map(|export| (export.path(), export)).collect(),
734                            );
735                        },
736                    }
737                }
738                let mast_forest =
739                    mast_forest.ok_or_else(|| serde::de::Error::missing_field("mast_forest"))?;
740                let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
741                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
742            }
743        }
744
745        deserializer.deserialize_struct("Library", &["mast_forest", "exports"], LibraryVisitor)
746    }
747}
748
749/// NOTE: Serialization of libraries is likely to be deprecated in a future release
750impl Serializable for KernelLibrary {
751    fn write_into<W: ByteWriter>(&self, target: &mut W) {
752        let Self { kernel: _, kernel_info: _, library } = self;
753
754        library.write_into(target);
755    }
756}
757
758/// NOTE: Serialization of libraries is likely to be deprecated in a future release
759impl Deserializable for KernelLibrary {
760    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
761        let library = Library::read_from(source)?;
762
763        Self::try_from(library).map_err(|err| {
764            DeserializationError::InvalidValue(format!(
765                "Failed to deserialize kernel library: {err}"
766            ))
767        })
768    }
769}
770
771/// NOTE: Deserialization is handled in the implementation for [Library]
772impl Serializable for LibraryExport {
773    fn write_into<W: ByteWriter>(&self, target: &mut W) {
774        match self {
775            LibraryExport::Procedure(ProcedureExport {
776                node,
777                path: name,
778                signature,
779                attributes,
780            }) => {
781                target.write_u8(0);
782                name.write_into(target);
783                target.write_u32(u32::from(*node));
784                if let Some(sig) = signature {
785                    target.write_bool(true);
786                    FunctionTypeSerializer(sig).write_into(target);
787                } else {
788                    target.write_bool(false);
789                }
790                attributes.write_into(target);
791            },
792            LibraryExport::Constant(ConstantExport { path: name, value }) => {
793                target.write_u8(1);
794                name.write_into(target);
795                value.write_into(target);
796            },
797            LibraryExport::Type(TypeExport { path: name, ty }) => {
798                target.write_u8(2);
799                name.write_into(target);
800                TypeSerializer(ty).write_into(target);
801            },
802        }
803    }
804}
805
806/// A wrapper type for [FunctionType] that provides serialization support via the winter-utils
807/// serializer.
808///
809/// This is a temporary implementation to allow type information to be serialized with libraries,
810/// but in a future release we'll either rely on the `serde` serialization for these types, or
811/// provide the serialization implementation in midenc-hir-type instead
812pub struct FunctionTypeSerializer<'a>(pub &'a FunctionType);
813
814impl Serializable for FunctionTypeSerializer<'_> {
815    fn write_into<W: ByteWriter>(&self, target: &mut W) {
816        target.write_u8(self.0.abi as u8);
817        target.write_usize(self.0.params().len());
818        target.write_many(self.0.params().iter().map(TypeSerializer));
819        target.write_usize(self.0.results().len());
820        target.write_many(self.0.results().iter().map(TypeSerializer));
821    }
822}
823
824/// A wrapper type for [FunctionType] that provides deserialization support via the winter-utils
825/// serializer.
826///
827/// This is a temporary implementation to allow type information to be serialized with libraries,
828/// but in a future release we'll either rely on the `serde` serialization for these types, or
829/// provide the serialization implementation in midenc-hir-type instead
830pub struct FunctionTypeDeserializer(pub FunctionType);
831
832impl Deserializable for FunctionTypeDeserializer {
833    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
834        use midenc_hir_type::CallConv;
835
836        let abi = match source.read_u8()? {
837            0 => CallConv::Fast,
838            1 => CallConv::SystemV,
839            2 => CallConv::Wasm,
840            3 => CallConv::CanonLift,
841            4 => CallConv::CanonLower,
842            5 => CallConv::Kernel,
843            invalid => {
844                return Err(DeserializationError::InvalidValue(format!(
845                    "invalid CallConv tag: {invalid}"
846                )));
847            },
848        };
849
850        let arity = source.read_usize()?;
851        let mut params = SmallVec::<[Type; 4]>::with_capacity(arity);
852        for _ in 0..arity {
853            let ty = TypeDeserializer::read_from(source)?.0;
854            params.push(ty);
855        }
856
857        let num_results = source.read_usize()?;
858        let mut results = SmallVec::<[Type; 1]>::with_capacity(num_results);
859        for _ in 0..num_results {
860            let ty = TypeDeserializer::read_from(source)?.0;
861            results.push(ty);
862        }
863
864        Ok(Self(FunctionType { abi, params, results }))
865    }
866}
867
868/// A wrapper type for [Type] that provides serialization support via the winter-utils serializer.
869///
870/// This is a temporary implementation to allow type information to be serialized with libraries,
871/// but in a future release we'll either rely on the `serde` serialization for these types, or
872/// provide the serialization implementation in midenc-hir-type instead
873pub struct TypeSerializer<'a>(pub &'a Type);
874
875impl Serializable for TypeSerializer<'_> {
876    fn write_into<W: ByteWriter>(&self, target: &mut W) {
877        use midenc_hir_type::{AddressSpace, TypeRepr};
878
879        match self.0 {
880            Type::Unknown => target.write_u8(0),
881            Type::Never => target.write_u8(1),
882            Type::I1 => target.write_u8(2),
883            Type::I8 => target.write_u8(3),
884            Type::U8 => target.write_u8(4),
885            Type::I16 => target.write_u8(5),
886            Type::U16 => target.write_u8(6),
887            Type::I32 => target.write_u8(7),
888            Type::U32 => target.write_u8(8),
889            Type::I64 => target.write_u8(9),
890            Type::U64 => target.write_u8(10),
891            Type::I128 => target.write_u8(11),
892            Type::U128 => target.write_u8(12),
893            Type::U256 => target.write_u8(13),
894            Type::F64 => target.write_u8(14),
895            Type::Felt => target.write_u8(15),
896            Type::Ptr(ty) => {
897                target.write_u8(16);
898                match ty.addrspace {
899                    AddressSpace::Byte => target.write_u8(0),
900                    AddressSpace::Element => target.write_u8(1),
901                }
902                TypeSerializer(&ty.pointee).write_into(target);
903            },
904            Type::Struct(ty) => {
905                target.write_u8(17);
906                match ty.repr() {
907                    TypeRepr::Default => target.write_u8(0),
908                    TypeRepr::Align(align) => {
909                        target.write_u8(1);
910                        target.write_u16(align.get());
911                    },
912                    TypeRepr::Packed(align) => {
913                        target.write_u8(2);
914                        target.write_u16(align.get());
915                    },
916                    TypeRepr::Transparent => target.write_u8(3),
917                    TypeRepr::BigEndian => target.write_u8(4),
918                }
919                target.write_u8(ty.len() as u8);
920                for field in ty.fields() {
921                    TypeSerializer(&field.ty).write_into(target);
922                }
923            },
924            Type::Array(ty) => {
925                target.write_u8(18);
926                target.write_usize(ty.len);
927                TypeSerializer(&ty.ty).write_into(target);
928            },
929            Type::List(ty) => {
930                target.write_u8(19);
931                TypeSerializer(ty).write_into(target);
932            },
933            Type::Function(ty) => {
934                target.write_u8(20);
935                FunctionTypeSerializer(ty).write_into(target);
936            },
937        }
938    }
939}
940
941/// A wrapper type for [Type] that provides deserialization support via the winter-utils serializer.
942///
943/// This is a temporary implementation to allow type information to be serialized with libraries,
944/// but in a future release we'll either rely on the `serde` serialization for these types, or
945/// provide the serialization implementation in midenc-hir-type instead
946pub struct TypeDeserializer(pub Type);
947
948impl Deserializable for TypeDeserializer {
949    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
950        use alloc::string::ToString;
951        use core::num::NonZeroU16;
952
953        use midenc_hir_type::{AddressSpace, ArrayType, PointerType, StructType, TypeRepr};
954
955        let ty = match source.read_u8()? {
956            0 => Type::Unknown,
957            1 => Type::Never,
958            2 => Type::I1,
959            3 => Type::I8,
960            4 => Type::U8,
961            5 => Type::I16,
962            6 => Type::U16,
963            7 => Type::I32,
964            8 => Type::U32,
965            9 => Type::I64,
966            10 => Type::U64,
967            11 => Type::I128,
968            12 => Type::U128,
969            13 => Type::U256,
970            14 => Type::F64,
971            15 => Type::Felt,
972            16 => {
973                let addrspace = match source.read_u8()? {
974                    0 => AddressSpace::Byte,
975                    1 => AddressSpace::Element,
976                    invalid => {
977                        return Err(DeserializationError::InvalidValue(format!(
978                            "invalid AddressSpace tag: {invalid}"
979                        )));
980                    },
981                };
982                let pointee = TypeDeserializer::read_from(source)?.0;
983                Type::Ptr(Arc::new(PointerType { addrspace, pointee }))
984            },
985            17 => {
986                let repr = match source.read_u8()? {
987                    0 => TypeRepr::Default,
988                    1 => {
989                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
990                            DeserializationError::InvalidValue(
991                                "invalid type repr: alignment must be a non-zero value".to_string(),
992                            )
993                        })?;
994                        TypeRepr::Align(align)
995                    },
996                    2 => {
997                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
998                            DeserializationError::InvalidValue(
999                                "invalid type repr: packed alignment must be a non-zero value"
1000                                    .to_string(),
1001                            )
1002                        })?;
1003                        TypeRepr::Packed(align)
1004                    },
1005                    3 => TypeRepr::Transparent,
1006                    invalid => {
1007                        return Err(DeserializationError::InvalidValue(format!(
1008                            "invalid TypeRepr tag: {invalid}"
1009                        )));
1010                    },
1011                };
1012                let num_fields = source.read_u8()?;
1013                let mut fields = SmallVec::<[Type; 4]>::with_capacity(num_fields as usize);
1014                for _ in 0..num_fields {
1015                    let ty = TypeDeserializer::read_from(source)?.0;
1016                    fields.push(ty);
1017                }
1018                Type::Struct(Arc::new(StructType::new_with_repr(repr, fields)))
1019            },
1020            18 => {
1021                let arity = source.read_usize()?;
1022                let ty = TypeDeserializer::read_from(source)?.0;
1023                Type::Array(Arc::new(ArrayType { ty, len: arity }))
1024            },
1025            19 => {
1026                let ty = TypeDeserializer::read_from(source)?.0;
1027                Type::List(Arc::new(ty))
1028            },
1029            20 => Type::Function(Arc::new(FunctionTypeDeserializer::read_from(source)?.0)),
1030            invalid => {
1031                return Err(DeserializationError::InvalidValue(format!(
1032                    "invalid Type tag: {invalid}"
1033                )));
1034            },
1035        };
1036        Ok(Self(ty))
1037    }
1038}
1039
1040#[cfg(feature = "arbitrary")]
1041impl proptest::prelude::Arbitrary for Library {
1042    type Parameters = ();
1043
1044    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1045        use miden_core::{
1046            Operation,
1047            mast::{BasicBlockNodeBuilder, MastForestContributor},
1048        };
1049        use proptest::prelude::*;
1050
1051        prop::collection::vec(any::<LibraryExport>(), 1..5)
1052            .prop_map(|exports| {
1053                let mut exports =
1054                    BTreeMap::from_iter(exports.into_iter().map(|export| (export.path(), export)));
1055                // Create a MastForest with actual nodes for the exports
1056                let mut mast_forest = MastForest::new();
1057                let mut nodes = Vec::new();
1058
1059                for export in exports.values() {
1060                    if let LibraryExport::Procedure(export) = export {
1061                        let node_id = BasicBlockNodeBuilder::new(
1062                            vec![Operation::Add, Operation::Mul],
1063                            Vec::new(),
1064                        )
1065                        .add_to_forest(&mut mast_forest)
1066                        .unwrap();
1067                        nodes.push((export.node, node_id));
1068                    }
1069                }
1070
1071                // Replace the export node IDs with the actual node IDs we created
1072                let mut procedure_exports = 0;
1073                for export in exports.values_mut() {
1074                    match export {
1075                        LibraryExport::Procedure(export) => {
1076                            procedure_exports += 1;
1077                            // Find the corresponding node we created
1078                            if let Some(&(_, actual_node_id)) =
1079                                nodes.iter().find(|(original_id, _)| *original_id == export.node)
1080                            {
1081                                export.node = actual_node_id;
1082                            } else {
1083                                // If we can't find the node (shouldn't happen), use the first node
1084                                // we created
1085                                if let Some(&(_, first_node_id)) = nodes.first() {
1086                                    export.node = first_node_id;
1087                                } else {
1088                                    // This should never happen since we create nodes for each
1089                                    // export
1090                                    panic!("No nodes created for exports");
1091                                }
1092                            }
1093                        },
1094                        LibraryExport::Constant(_) | LibraryExport::Type(_) => (),
1095                    }
1096                }
1097
1098                let mut node_ids = Vec::with_capacity(procedure_exports);
1099                for export in exports.values() {
1100                    if let LibraryExport::Procedure(export) = export {
1101                        // Add the node to the forest roots if it's not already there
1102                        mast_forest.make_root(export.node);
1103                        // Collect the node id for recomputing the digest
1104                        node_ids.push(export.node);
1105                    }
1106                }
1107
1108                // Recompute the digest
1109                let digest = mast_forest.compute_nodes_commitment(&node_ids);
1110
1111                let mast_forest = Arc::new(mast_forest);
1112                Library { digest, exports, mast_forest }
1113            })
1114            .boxed()
1115    }
1116
1117    type Strategy = proptest::prelude::BoxedStrategy<Self>;
1118}