-
Notifications
You must be signed in to change notification settings - Fork 123
refactor: minor improvements to AccountComponentCode
#2597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from 10 commits
b83cefe
3094e94
956d014
477ad21
cfbe27e
93cf2ee
b97861c
ea2fde3
b0a9f65
e4168d4
b8f1786
b552602
caf8e6d
c4efa3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,33 +1,125 @@ | ||
| use miden_assembly::Library; | ||
| use miden_processor::mast::MastForest; | ||
| use alloc::collections::BTreeMap; | ||
| use alloc::sync::Arc; | ||
| use alloc::vec::Vec; | ||
|
|
||
| use miden_assembly::library::ProcedureExport; | ||
| use miden_assembly::{Library, Path}; | ||
| use miden_core::Word; | ||
| use miden_core::mast::{ExternalNodeBuilder, MastForest, MastForestContributor, MastNodeExt}; | ||
|
|
||
| use crate::account::AccountProcedureRoot; | ||
| use crate::vm::AdviceMap; | ||
|
|
||
| // ACCOUNT COMPONENT CODE | ||
| // ================================================================================================ | ||
|
|
||
| /// A [`Library`] that has been assembled for use as component code. | ||
| /// The code associated with an account component, consisting of a [`MastForest`] and the set of | ||
| /// procedures exported by the component. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct AccountComponentCode(Library); | ||
| pub struct AccountComponentCode { | ||
| mast: Arc<MastForest>, | ||
| exports: Vec<ProcedureExport>, | ||
| } | ||
|
||
|
|
||
| impl AccountComponentCode { | ||
| /// Returns a reference to the underlying [`Library`] | ||
| pub fn as_library(&self) -> &Library { | ||
| &self.0 | ||
| // CONSTRUCTORS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Creates a new [`AccountComponentCode`] from the provided [`Library`]. | ||
| pub fn from_library(library: Library) -> Self { | ||
| let mast = library.mast_forest().clone(); | ||
| let exports: Vec<ProcedureExport> = | ||
| library.exports().filter_map(|export| export.as_procedure().cloned()).collect(); | ||
|
|
||
| Self { mast, exports } | ||
| } | ||
|
|
||
| /// Returns a reference to the code's [`MastForest`] | ||
| pub fn mast_forest(&self) -> &MastForest { | ||
| self.0.mast_forest().as_ref() | ||
| /// Returns a new [`AccountComponentCode`] containing only external node references to the | ||
| /// procedures in the provided [`Library`]. | ||
| /// | ||
| /// Note: This method creates a minimal [`MastForest`] where each exported procedure is | ||
| /// represented by an external node referencing its digest, rather than copying the entire | ||
| /// library's MAST forest. The actual procedure code will be resolved at runtime via the | ||
| /// `MastForestStore`. | ||
| pub fn from_library_reference(library: &Library) -> Self { | ||
|
||
| let mut mast = MastForest::new(); | ||
| let mut exports = Vec::new(); | ||
|
|
||
| for export in library.exports() { | ||
| if let Some(proc_export) = export.as_procedure() { | ||
| // Get the digest of the procedure from the library | ||
| let digest = library.mast_forest()[proc_export.node].digest(); | ||
|
|
||
| // Create an external node referencing the digest | ||
| let node_id = ExternalNodeBuilder::new(digest) | ||
| .add_to_forest(&mut mast) | ||
| .expect("adding external node to forest should not fail"); | ||
| mast.make_root(node_id); | ||
|
|
||
| exports.push(ProcedureExport { | ||
| node: node_id, | ||
| path: proc_export.path.clone(), | ||
| signature: proc_export.signature.clone(), | ||
| attributes: proc_export.attributes.clone(), | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| Self { mast: Arc::new(mast), exports } | ||
| } | ||
|
|
||
| /// Consumes `self` and returns the underlying [`Library`] | ||
| pub fn into_library(self) -> Library { | ||
| self.0 | ||
| /// Creates a new [`AccountComponentCode`] from the provided MAST forest and procedure exports. | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics if any of the exported procedure node IDs are not found in the MAST forest. | ||
| pub fn from_parts(mast: Arc<MastForest>, exports: Vec<ProcedureExport>) -> Self { | ||
| for export in &exports { | ||
| assert!( | ||
| mast.get_node_by_id(export.node).is_some(), | ||
| "exported procedure node not found in the MAST forest" | ||
| ); | ||
| } | ||
|
|
||
| Self { mast, exports } | ||
| } | ||
|
|
||
| /// Returns a new [AccountComponentCode] with the provided advice map entries merged into the | ||
| /// underlying [Library]'s [MastForest]. | ||
| // PUBLIC ACCESSORS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns the [`MastForest`] wrapped in an [`Arc`]. | ||
| pub fn mast(&self) -> Arc<MastForest> { | ||
| self.mast.clone() | ||
| } | ||
|
|
||
| /// Returns the procedure exports of this component. | ||
| pub fn exports(&self) -> &[ProcedureExport] { | ||
| &self.exports | ||
| } | ||
igamigo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// Returns an iterator over the [`AccountProcedureRoot`]s of this component's exported | ||
| /// procedures. | ||
| pub fn procedure_roots(&self) -> impl Iterator<Item = AccountProcedureRoot> + '_ { | ||
| self.exports | ||
| .iter() | ||
| .map(|export| AccountProcedureRoot::from_raw(self.mast[export.node].digest())) | ||
| } | ||
|
|
||
| /// Returns the digest of the procedure with the specified path, or `None` if it was not found | ||
| /// in this component. | ||
| pub fn get_procedure_root_by_path(&self, path: impl AsRef<Path>) -> Option<Word> { | ||
| let path = path.as_ref().to_absolute(); | ||
| self.exports | ||
| .iter() | ||
| .find(|export| export.path.as_ref() == path.as_ref()) | ||
| .map(|export| self.mast[export.node].digest()) | ||
| } | ||
igamigo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // MUTATORS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns a new [`AccountComponentCode`] with the provided advice map entries merged into the | ||
| /// underlying [`MastForest`]. | ||
| /// | ||
| /// This allows adding advice map entries to an already-compiled account component, | ||
| /// which is useful when the entries are determined after compilation. | ||
|
|
@@ -36,28 +128,39 @@ impl AccountComponentCode { | |
| return self; | ||
| } | ||
|
|
||
| Self(self.0.with_advice_map(advice_map)) | ||
| let mut mast = Arc::unwrap_or_clone(self.mast); | ||
| mast.advice_map_mut().extend(advice_map); | ||
|
|
||
| Self { | ||
| mast: Arc::new(mast), | ||
| exports: self.exports, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl AsRef<Library> for AccountComponentCode { | ||
| fn as_ref(&self) -> &Library { | ||
| self.as_library() | ||
| /// Clears the debug info from the underlying [`MastForest`]. | ||
| pub fn clear_debug_info(&mut self) { | ||
| let mut mast = self.mast.clone(); | ||
| Arc::make_mut(&mut mast).clear_debug_info(); | ||
| self.mast = mast; | ||
| } | ||
| } | ||
|
|
||
| // CONVERSIONS | ||
| // ================================================================================================ | ||
|
|
||
| impl From<Library> for AccountComponentCode { | ||
| fn from(value: Library) -> Self { | ||
| Self(value) | ||
| fn from(library: Library) -> Self { | ||
| Self::from_library(library) | ||
| } | ||
| } | ||
|
|
||
| impl From<AccountComponentCode> for Library { | ||
| fn from(value: AccountComponentCode) -> Self { | ||
| value.into_library() | ||
| let exports: BTreeMap<_, _> = | ||
| value.exports.into_iter().map(|e| (e.path.clone(), e.into())).collect(); | ||
|
|
||
| Library::new(value.mast, exports) | ||
| .expect("AccountComponentCode should have at least one export") | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -79,13 +182,12 @@ mod tests { | |
| .expect("failed to assemble library"); | ||
| let component_code = AccountComponentCode::from(library); | ||
|
|
||
| assert!(component_code.mast_forest().advice_map().is_empty()); | ||
| assert!(component_code.mast().advice_map().is_empty()); | ||
|
|
||
| // Empty advice map should be a no-op (digest stays the same) | ||
| let cloned = component_code.clone(); | ||
| let original_digest = cloned.as_library().digest(); | ||
| let original_digest = *Library::from(component_code.clone()).digest(); | ||
| let component_code = component_code.with_advice_map(AdviceMap::default()); | ||
| assert_eq!(original_digest, component_code.as_library().digest()); | ||
| assert_eq!(&original_digest, Library::from(component_code.clone()).digest()); | ||
|
|
||
| // Non-empty advice map should add entries | ||
| let key = Word::from([10u32, 20, 30, 40]); | ||
|
|
@@ -95,7 +197,7 @@ mod tests { | |
|
|
||
| let component_code = component_code.with_advice_map(advice_map); | ||
|
|
||
| let mast = component_code.mast_forest(); | ||
| let mast = component_code.mast(); | ||
| let stored = mast.advice_map().get(&key).expect("entry should be present"); | ||
| assert_eq!(stored.as_ref(), value.as_slice()); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,7 +37,7 @@ pub use protocol::ProtocolLib; | |
| pub mod assembly { | ||
| pub use miden_assembly::ast::{Module, ModuleKind, ProcedureName, QualifiedProcedureName}; | ||
| pub use miden_assembly::debuginfo::SourceManagerSync; | ||
| pub use miden_assembly::library::LibraryExport; | ||
| pub use miden_assembly::library::{LibraryExport, ProcedureExport as LibraryProcedureExport}; | ||
|
||
| pub use miden_assembly::{ | ||
| Assembler, | ||
| DefaultSourceManager, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -461,7 +461,7 @@ fn test_custom_account_custom_notes() { | |
| end | ||
| "; | ||
| let note_script = CodeBuilder::default() | ||
| .with_dynamically_linked_library(account_component.component_code()) | ||
| .with_dynamically_linked_library(account_component.component_code().clone()) | ||
| .unwrap() | ||
| .compile_note_script(compatible_source_code) | ||
| .unwrap(); | ||
|
|
@@ -488,7 +488,7 @@ fn test_custom_account_custom_notes() { | |
| end | ||
| "; | ||
| let note_script = CodeBuilder::default() | ||
| .with_dynamically_linked_library(account_component.component_code()) | ||
| .with_dynamically_linked_library(account_component.component_code().clone()) | ||
| .unwrap() | ||
| .compile_note_script(incompatible_source_code) | ||
| .unwrap(); | ||
|
|
@@ -571,7 +571,7 @@ fn test_custom_account_multiple_components_custom_notes() { | |
| end | ||
| "; | ||
| let note_script = CodeBuilder::default() | ||
| .with_dynamically_linked_library(custom_component.component_code()) | ||
| .with_dynamically_linked_library(custom_component.component_code().clone()) | ||
| .unwrap() | ||
| .compile_note_script(compatible_source_code) | ||
| .unwrap(); | ||
|
|
@@ -609,7 +609,7 @@ fn test_custom_account_multiple_components_custom_notes() { | |
| end | ||
| "; | ||
| let note_script = CodeBuilder::default() | ||
| .with_dynamically_linked_library(custom_component.component_code()) | ||
| .with_dynamically_linked_library(custom_component.component_code().clone()) | ||
|
||
| .unwrap() | ||
| .compile_note_script(incompatible_source_code) | ||
| .unwrap(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.