diff --git a/assembly/src/assembler/mod.rs b/assembly/src/assembler/mod.rs index ba0bd395d4..310f3cfca8 100644 --- a/assembly/src/assembler/mod.rs +++ b/assembly/src/assembler/mod.rs @@ -146,6 +146,12 @@ impl Assembler { Ok(()) } + /// Adds the compiled library to provide modules for the compilation. + pub fn with_compiled_library(mut self, library: CompiledLibrary) -> Result { + self.add_compiled_library(library)?; + Ok(self) + } + /// Adds the library to provide modules for the compilation. pub fn with_library(mut self, library: &L) -> Result where diff --git a/assembly/src/library/error.rs b/assembly/src/library/error.rs index 7aef252b67..71900185d7 100644 --- a/assembly/src/library/error.rs +++ b/assembly/src/library/error.rs @@ -79,9 +79,7 @@ pub enum CompiledLibraryError { procedure_path: FullyQualifiedProcedureName, }, #[error("exports are not in the same namespace. All namespaces: {namespaces:?}")] - InconsistentNamespaces { - namespaces: Vec - }, + InconsistentNamespaces { namespaces: Vec }, #[error(transparent)] Kernel(#[from] KernelError), } diff --git a/assembly/src/library/mod.rs b/assembly/src/library/mod.rs index a3b04bf66c..9c88acddbf 100644 --- a/assembly/src/library/mod.rs +++ b/assembly/src/library/mod.rs @@ -28,6 +28,7 @@ mod tests; // ================================================================================================ /// Represents a library where all modules were compiled into a [`MastForest`]. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct CompiledLibrary { mast_forest: MastForest, // a path for every `root` in the associated MAST forest @@ -172,6 +173,18 @@ impl CompiledLibrary { } } +impl Serializable for CompiledLibrary { + fn write_into(&self, target: &mut W) { + self.write_into_with_options(target, AstSerdeOptions::default()) + } +} + +impl Deserializable for CompiledLibrary { + fn read_from(source: &mut R) -> Result { + Self::read_from_with_options(source, AstSerdeOptions::default()) + } +} + #[cfg(feature = "std")] mod use_std_library { use super::*; @@ -181,6 +194,7 @@ mod use_std_library { use masl::{LibraryEntry, WalkLibrary}; use miette::{Context, Report}; use std::{fs, io, path::Path}; + use vm_core::utils::ReadAdapter; impl CompiledLibrary { /// File extension for the Assembly Library. @@ -304,6 +318,19 @@ mod use_std_library { Assembler::default().assemble_library(modules.into_values()) } + + pub fn deserialize_from_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + let mut file = fs::File::open(path).map_err(|err| { + DeserializationError::InvalidValue(format!( + "failed to open file at {}: {err}", + path.to_string_lossy() + )) + })?; + let mut adapter = ReadAdapter::new(&mut file); + + Self::read_from(&mut adapter) + } } } // KERNEL LIBRARY diff --git a/assembly/src/library/tests.rs b/assembly/src/library/tests.rs index 476f96f6c6..2ee49b698f 100644 --- a/assembly/src/library/tests.rs +++ b/assembly/src/library/tests.rs @@ -1,12 +1,14 @@ use alloc::{string::ToString, sync::Arc, vec::Vec}; -use vm_core::utils::{Deserializable, Serializable, SliceReader}; +use vm_core::utils::SliceReader; -use super::{Library, LibraryNamespace, LibraryPath, MaslLibrary, Version}; +use super::LibraryPath; use crate::{ - ast::{AstSerdeOptions, Module, ModuleKind}, + ast::{AstSerdeOptions, Module, ModuleKind, ProcedureName}, diagnostics::{IntoDiagnostic, Report, SourceFile}, + library::CompiledLibrary, testing::TestContext, + Assembler, }; macro_rules! parse_module { @@ -44,23 +46,28 @@ fn masl_locations_serialization() -> Result<(), Report> { let modules = vec![foo, bar]; // serialize/deserialize the bundle with locations - let namespace = LibraryNamespace::new("test").unwrap(); - let version = Version::min(); - let bundle = MaslLibrary::new(namespace, version, modules.iter().cloned(), Vec::new())?; + let bundle = Assembler::default().assemble_library(modules.iter().cloned()).unwrap(); let mut bytes = Vec::new(); - bundle.write_into(&mut bytes); - let deserialized = MaslLibrary::read_from(&mut SliceReader::new(&bytes)).unwrap(); + bundle.write_into_with_options(&mut bytes, AstSerdeOptions::new(true, true)); + let deserialized = CompiledLibrary::read_from_with_options( + &mut SliceReader::new(&bytes), + AstSerdeOptions::new(true, false), + ) + .unwrap(); assert_eq!(bundle, deserialized); // serialize/deserialize the bundle without locations - let namespace = LibraryNamespace::new("test").unwrap(); - let bundle = MaslLibrary::new(namespace, version, modules, Vec::new())?; + let bundle = Assembler::default().assemble_library(modules.iter().cloned()).unwrap(); // serialize/deserialize the bundle let mut bytes = Vec::new(); bundle.write_into_with_options(&mut bytes, AstSerdeOptions::new(true, false)); - let deserialized = MaslLibrary::read_from(&mut SliceReader::new(&bytes)).unwrap(); + let deserialized = CompiledLibrary::read_from_with_options( + &mut SliceReader::new(&bytes), + AstSerdeOptions::new(true, false), + ) + .unwrap(); assert_eq!(bundle, deserialized); Ok(()) @@ -79,20 +86,13 @@ fn get_module_by_path() -> Result<(), Report> { let modules = vec![foo]; // create the bundle with locations - let namespace = LibraryNamespace::new("test")?; - let version = Version::min(); - let bundle = MaslLibrary::new(namespace, version, modules, Vec::new())?; + let bundle = Assembler::default().assemble_library(modules.iter().cloned()).unwrap(); - // get AST associated with "test::foo" path - let foo_ast = bundle.get_module(&LibraryPath::new("test::foo").unwrap()).unwrap(); - let foo_expected = "export.foo - add -end + let foo_module_info = bundle.into_module_infos().next().unwrap(); + assert_eq!(foo_module_info.path(), &LibraryPath::new("test::foo").unwrap()); -"; - assert_eq!(foo_ast.to_string(), foo_expected); - - assert!(bundle.get_module(&LibraryPath::new("test::bar").unwrap()).is_none()); + let (_, foo_proc) = foo_module_info.procedure_infos().next().unwrap(); + assert_eq!(foo_proc.name, ProcedureName::new("foo").unwrap()); Ok(()) } diff --git a/miden/benches/program_compilation.rs b/miden/benches/program_compilation.rs index 4462b7231d..970a722411 100644 --- a/miden/benches/program_compilation.rs +++ b/miden/benches/program_compilation.rs @@ -15,9 +15,11 @@ fn program_compilation(c: &mut Criterion) { exec.sha256::hash_2to1 end"; bench.iter(|| { - let assembler = Assembler::default() - .with_library(&StdLibrary::default()) + let mut assembler = Assembler::default(); + assembler + .add_compiled_library(StdLibrary::default().into()) .expect("failed to load stdlib"); + assembler.assemble_program(source).expect("Failed to compile test source.") }); }); diff --git a/miden/benches/program_execution.rs b/miden/benches/program_execution.rs index 4487672ca9..34a183b625 100644 --- a/miden/benches/program_execution.rs +++ b/miden/benches/program_execution.rs @@ -15,8 +15,9 @@ fn program_execution(c: &mut Criterion) { begin exec.sha256::hash_2to1 end"; - let assembler = Assembler::default() - .with_library(&StdLibrary::default()) + let mut assembler = Assembler::default(); + assembler + .add_compiled_library(StdLibrary::default().into()) .expect("failed to load stdlib"); let program: Program = assembler.assemble_program(source).expect("Failed to compile test source."); diff --git a/miden/src/cli/bundle.rs b/miden/src/cli/bundle.rs index 83d87abaec..bb5342d5fd 100644 --- a/miden/src/cli/bundle.rs +++ b/miden/src/cli/bundle.rs @@ -1,6 +1,8 @@ use assembly::{ + ast::AstSerdeOptions, diagnostics::{IntoDiagnostic, Report}, - LibraryNamespace, MaslLibrary, Version, + library::CompiledLibrary, + LibraryNamespace, Version, }; use clap::Parser; use std::path::PathBuf; @@ -40,11 +42,13 @@ impl BundleCmd { let library_namespace = namespace.parse::().expect("invalid base namespace"); - let version = self.version.parse::().expect("invalid cargo version"); - let stdlib = MaslLibrary::read_from_dir(&self.dir, library_namespace, version)?; + // TODO: Add version to `Library` + let _version = self.version.parse::().expect("invalid cargo version"); + let stdlib = CompiledLibrary::from_dir(&self.dir, library_namespace)?; // write the masl output - stdlib.write_to_dir(self.dir.clone()).into_diagnostic()?; + let options = AstSerdeOptions::new(false, false); + stdlib.write_to_dir(self.dir.clone(), options).into_diagnostic()?; println!("Built library {}", namespace); diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 76355a3601..90057ee37f 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -1,7 +1,8 @@ use assembly::{ ast::{Module, ModuleKind}, diagnostics::{IntoDiagnostic, Report, WrapErr}, - Assembler, Library, LibraryNamespace, MaslLibrary, + library::CompiledLibrary, + Assembler, LibraryNamespace, }; use miden_vm::{ crypto::{MerkleStore, MerkleTree, NodeIndex, PartialMerkleTree, RpoDigest, SimpleSmt}, @@ -404,20 +405,21 @@ impl ProgramFile { /// Compiles this program file into a [Program]. #[instrument(name = "compile_program", skip_all)] - pub fn compile<'a, I, L>(&self, debug: &Debug, libraries: I) -> Result + pub fn compile<'a, I>(&self, debug: &Debug, libraries: I) -> Result where - I: IntoIterator, - L: ?Sized + Library + 'static, + I: IntoIterator, { // compile program - let mut assembler = Assembler::default() - .with_debug_mode(debug.is_on()) - .with_library(&StdLibrary::default()) + let mut assembler = Assembler::default().with_debug_mode(debug.is_on()); + assembler + .add_compiled_library(StdLibrary::default().into()) .wrap_err("Failed to load stdlib")?; - assembler = assembler - .with_libraries(libraries.into_iter()) - .wrap_err("Failed to load libraries")?; + for library in libraries { + assembler + .add_compiled_library(library.clone()) + .wrap_err("Failed to load libraries")?; + } let program: Program = assembler .assemble_program(self.ast.as_ref()) @@ -529,7 +531,7 @@ impl ProgramHash { // LIBRARY FILE // ================================================================================================ pub struct Libraries { - pub libraries: Vec, + pub libraries: Vec, } impl Libraries { @@ -543,7 +545,9 @@ impl Libraries { let mut libraries = Vec::new(); for path in paths { - let library = MaslLibrary::read_from_file(path)?; + // TODO(plafer): How to create a `Report` from an error that doesn't derive + // `Diagnostic`? + let library = CompiledLibrary::deserialize_from_file(path).unwrap(); libraries.push(library); } diff --git a/miden/src/examples/blake3.rs b/miden/src/examples/blake3.rs index 7bae853923..07169dbc34 100644 --- a/miden/src/examples/blake3.rs +++ b/miden/src/examples/blake3.rs @@ -45,7 +45,7 @@ fn generate_blake3_program(n: usize) -> Program { ); Assembler::default() - .with_library(&StdLibrary::default()) + .with_compiled_library(StdLibrary::default().into()) .unwrap() .assemble_program(program) .unwrap() diff --git a/miden/src/repl/mod.rs b/miden/src/repl/mod.rs index 1139f20e42..225be66ea4 100644 --- a/miden/src/repl/mod.rs +++ b/miden/src/repl/mod.rs @@ -1,4 +1,4 @@ -use assembly::{Assembler, Library, MaslLibrary}; +use assembly::{library::CompiledLibrary, Assembler}; use miden_vm::{math::Felt, DefaultHost, StackInputs, Word}; use processor::ContextId; use rustyline::{error::ReadlineError, DefaultEditor}; @@ -151,13 +151,13 @@ pub fn start_repl(library_paths: &Vec, use_stdlib: bool) { // load libraries from files let mut provided_libraries = Vec::new(); for path in library_paths { - let library = MaslLibrary::read_from_file(path) + let library = CompiledLibrary::deserialize_from_file(path) .map_err(|e| format!("Failed to read library: {e}")) .unwrap(); provided_libraries.push(library); } if use_stdlib { - provided_libraries.push(MaslLibrary::from(StdLibrary::default())); + provided_libraries.push(StdLibrary::default().into()); } println!("========================== Miden REPL ============================"); @@ -303,14 +303,16 @@ pub fn start_repl(library_paths: &Vec, use_stdlib: bool) { #[allow(clippy::type_complexity)] fn execute( program: String, - provided_libraries: &[MaslLibrary], + provided_libraries: &[CompiledLibrary], ) -> Result<(Vec<(u64, Word)>, Vec), String> { // compile program let mut assembler = Assembler::default(); - assembler = assembler - .with_libraries(provided_libraries.iter()) - .map_err(|err| format!("{err}"))?; + for library in provided_libraries { + assembler + .add_compiled_library(library.clone()) + .map_err(|err| format!("{err}"))?; + } let program = assembler.assemble_program(program).map_err(|err| format!("{err}"))?; @@ -357,7 +359,7 @@ fn read_mem_address(mem_str: &str) -> Result { /// all available modules if no module name was provided. fn handle_use_command( line: String, - provided_libraries: &Vec, + provided_libraries: &[CompiledLibrary], imported_modules: &mut BTreeSet, ) { let tokens: Vec<&str> = line.split_whitespace().collect(); @@ -365,8 +367,8 @@ fn handle_use_command( match tokens.len() { 1 => { println!("Modules available for importing:"); - for lib in provided_libraries { - lib.modules().for_each(|module| println!("{}", module.path())); + for lib in provided_libraries.iter().cloned() { + lib.into_module_infos().for_each(|module| println!("{}", module.path())); } } 2 => { diff --git a/miden/src/tools/mod.rs b/miden/src/tools/mod.rs index f4718ce014..09c7f67298 100644 --- a/miden/src/tools/mod.rs +++ b/miden/src/tools/mod.rs @@ -216,7 +216,7 @@ where { let program = Assembler::default() .with_debug_mode(true) - .with_library(&StdLibrary::default())? + .with_compiled_library(StdLibrary::default().into())? .assemble_program(program)?; let mut execution_details = ExecutionDetails::default(); diff --git a/miden/tests/integration/flow_control/mod.rs b/miden/tests/integration/flow_control/mod.rs index 9d2954f960..d520da6b03 100644 --- a/miden/tests/integration/flow_control/mod.rs +++ b/miden/tests/integration/flow_control/mod.rs @@ -394,7 +394,9 @@ fn simple_dyncall() { #[allow(unused)] #[test] fn procref() { - let mut assembler = Assembler::default().with_library(&StdLibrary::default()).unwrap(); + let mut assembler = Assembler::default() + .with_compiled_library(StdLibrary::default().into()) + .unwrap(); let module_source = " use.std::math::u64 diff --git a/stdlib/build.rs b/stdlib/build.rs index 7b059b5ff0..f469f1d000 100644 --- a/stdlib/build.rs +++ b/stdlib/build.rs @@ -1,5 +1,7 @@ use assembly::{ + ast::AstSerdeOptions, diagnostics::{IntoDiagnostic, Result}, + library::CompiledLibrary, LibraryNamespace, MaslLibrary, Version, }; use std::{env, fs, io, path::Path}; @@ -12,7 +14,7 @@ use md_renderer::MarkdownRenderer; const ASM_DIR_PATH: &str = "./asm"; const ASL_DIR_PATH: &str = "./assets"; -const DOC_DIR_PATH: &str = "./docs"; +const _DOC_DIR_PATH: &str = "./docs"; // PRE-PROCESSING // ================================================================================================ @@ -26,17 +28,20 @@ fn main() -> Result<()> { println!("cargo:rerun-if-changed=../assembly/src"); let namespace = "std".parse::().expect("invalid base namespace"); - let version = env!("CARGO_PKG_VERSION").parse::().expect("invalid cargo version"); - let stdlib = MaslLibrary::read_from_dir(ASM_DIR_PATH, namespace, version)?; + // TODO: Add version to `Library` + let _version = env!("CARGO_PKG_VERSION").parse::().expect("invalid cargo version"); + let stdlib = CompiledLibrary::from_dir(ASM_DIR_PATH, namespace)?; // write the masl output let build_dir = env::var("OUT_DIR").unwrap(); + let options = AstSerdeOptions::new(false, false); stdlib - .write_to_dir(Path::new(&build_dir).join(ASL_DIR_PATH)) + .write_to_dir(Path::new(&build_dir).join(ASL_DIR_PATH), options) .into_diagnostic()?; // updates the documentation of these modules - build_stdlib_docs(&stdlib, DOC_DIR_PATH).into_diagnostic()?; + // TODO(plafer): Reenable this + // build_stdlib_docs(stdlib, DOC_DIR_PATH).into_diagnostic()?; Ok(()) } diff --git a/stdlib/src/lib.rs b/stdlib/src/lib.rs index 8b6a7948b1..479a032ea8 100644 --- a/stdlib/src/lib.rs +++ b/stdlib/src/lib.rs @@ -2,18 +2,15 @@ extern crate alloc; -use assembly::{ - ast::Module, utils::Deserializable, Library, LibraryNamespace, LibraryPath, MaslLibrary, - Version, -}; +use assembly::{library::CompiledLibrary, utils::Deserializable}; // STANDARD LIBRARY // ================================================================================================ /// TODO: add docs -pub struct StdLibrary(MaslLibrary); +pub struct StdLibrary(CompiledLibrary); -impl From for MaslLibrary { +impl From for CompiledLibrary { fn from(value: StdLibrary) -> Self { value.0 } @@ -22,42 +19,27 @@ impl From for MaslLibrary { impl Default for StdLibrary { fn default() -> Self { let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/std.masl")); - let contents = MaslLibrary::read_from_bytes(bytes).expect("failed to read std masl!"); + let contents = CompiledLibrary::read_from_bytes(bytes).expect("failed to read std masl!"); Self(contents) } } -impl Library for StdLibrary { - fn root_ns(&self) -> &LibraryNamespace { - self.0.root_ns() - } +#[cfg(test)] +mod tests { + use assembly::LibraryPath; - fn version(&self) -> &Version { - self.0.version() - } + use super::*; - fn modules(&self) -> impl ExactSizeIterator + '_ { - self.0.modules() - } + #[test] + fn test_compile() { + let path = "std::math::u64::overflowing_add".parse::().unwrap(); + let stdlib = StdLibrary::default(); + let exists = stdlib.0.into_module_infos().any(|module| { + module + .procedure_infos() + .any(|(_, proc)| module.path().clone().append(&proc.name).unwrap() == path) + }); - fn dependencies(&self) -> &[assembly::LibraryNamespace] { - self.0.dependencies() + assert!(exists); } - - fn get_module(&self, path: &LibraryPath) -> Option<&Module> { - self.0.get_module(path) - } -} - -#[test] -fn test_compile() { - let path = "std::math::u64::overflowing_add".parse::().unwrap(); - let stdlib = StdLibrary::default(); - let exists = stdlib.modules().any(|module| { - module - .procedures() - .any(|proc| module.path().clone().append(proc.name()).unwrap() == path) - }); - - assert!(exists); } diff --git a/stdlib/tests/crypto/falcon.rs b/stdlib/tests/crypto/falcon.rs index 0220d143e9..823596ad8c 100644 --- a/stdlib/tests/crypto/falcon.rs +++ b/stdlib/tests/crypto/falcon.rs @@ -200,7 +200,7 @@ fn falcon_prove_verify() { let (source, op_stack, _, _, advice_map) = generate_test(sk, message); let program: Program = Assembler::default() - .with_library(&StdLibrary::default()) + .with_compiled_library(StdLibrary::default().into()) .expect("failed to load stdlib") .assemble_program(source) .expect("failed to compile test source"); diff --git a/stdlib/tests/mem/mod.rs b/stdlib/tests/mem/mod.rs index 95c75bf11e..bce2b146cc 100644 --- a/stdlib/tests/mem/mod.rs +++ b/stdlib/tests/mem/mod.rs @@ -23,7 +23,7 @@ fn test_memcopy() { "; let assembler = assembly::Assembler::default() - .with_library(&StdLibrary::default()) + .with_compiled_library(StdLibrary::default().into()) .expect("failed to load stdlib"); let program: Program = diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index 0a717511c6..d2ccc7ae61 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -8,6 +8,7 @@ extern crate std; // IMPORTS // ================================================================================================ +use assembly::library::CompiledLibrary; use processor::Program; #[cfg(not(target_family = "wasm"))] use proptest::prelude::{Arbitrary, Strategy}; @@ -177,7 +178,7 @@ pub struct Test { pub stack_inputs: StackInputs, pub advice_inputs: AdviceInputs, pub in_debug_mode: bool, - pub libraries: Vec, + pub libraries: Vec, pub add_modules: Vec<(LibraryPath, String)>, } @@ -284,7 +285,7 @@ impl Test { } else { assembly::Assembler::default() }; - let assembler = self + let mut assembler = self .add_modules .iter() .fold(assembler, |assembler, (path, source)| { @@ -295,9 +296,10 @@ impl Test { ) .expect("invalid masm source code") }) - .with_debug_mode(self.in_debug_mode) - .with_libraries(self.libraries.iter()) - .expect("failed to load stdlib"); + .with_debug_mode(self.in_debug_mode); + for library in &self.libraries { + assembler.add_compiled_library(library.clone()).unwrap(); + } assembler.assemble_program(self.source.clone()) }