use miden_mast_package::{
Dependency,
debug_info::{
DebugErrorMessage, DebugErrorMessagesSection, DebugSourceAsmOp, DebugSourceGraphSection,
DebugSourceMapSection, DebugSourceNode, DebugSourceNodeId, DebugSourceVar,
},
};
use super::*;
use crate::mast_forest_builder::SourceDebugGraph;
pub struct AssemblyProduct {
package: Box<Package>,
kernel_package: Option<Arc<Package>>,
debug_info: Option<DebugInfoSections>,
source_graph: Option<SourceDebugGraph>,
}
impl AssemblyProduct {
pub(super) fn new(
package: Box<Package>,
kernel: Option<Arc<Package>>,
debug_info: Option<DebugInfoSections>,
source_graph: Option<SourceDebugGraph>,
) -> Self {
assert!(
kernel.is_none() || !package.is_kernel(),
"kernels cannot depend on another kernel"
);
Self {
package,
kernel_package: kernel,
debug_info,
source_graph,
}
}
#[cfg_attr(not(feature = "std"), expect(unused))]
pub fn extend_dependencies(
&mut self,
deps: impl IntoIterator<Item = Dependency>,
) -> Result<(), Report> {
for dep in deps {
self.package.manifest.add_dependency(dep).map_err(Report::msg)?;
}
Ok(())
}
pub fn into_artifact(self) -> Result<Box<Package>, Report> {
let Self {
mut package,
kernel_package,
debug_info,
source_graph,
} = self;
if package.is_program()
&& let Some(kernel_package) = kernel_package
{
package.sections.push(linked_kernel_package_section(kernel_package.as_ref()));
if let Some(kernel_dep) =
package.manifest.dependencies().find(|dep| dep.id() == &kernel_package.name)
{
if kernel_dep.digest != kernel_package.digest()
|| kernel_dep.kind != kernel_package.kind
|| kernel_dep.version() != &kernel_package.version
{
return Err(Report::msg(format!(
"unable to register kernel dependency: '{}' already exists as a dependency, but with different metadata than the actual kernel package",
kernel_package.name
)));
}
} else {
package
.manifest
.add_dependency(Dependency {
name: kernel_package.name.clone(),
kind: kernel_package.kind,
version: kernel_package.version.clone(),
digest: kernel_package.digest(),
})
.map_err(|err| {
Report::msg(format!("unable to register kernel dependency: {err}"))
})?;
}
}
if let Some(DebugInfoSections {
debug_sources_section,
debug_functions_section,
debug_types_section,
}) = debug_info
{
package
.sections
.push(Section::new(SectionId::DEBUG_SOURCES, debug_sources_section.to_bytes()));
package
.sections
.push(Section::new(SectionId::DEBUG_FUNCTIONS, debug_functions_section.to_bytes()));
package
.sections
.push(Section::new(SectionId::DEBUG_TYPES, debug_types_section.to_bytes()));
}
if let Some(source_graph) = source_graph {
package.sections.push(Section::new(
SectionId::DEBUG_SOURCE_GRAPH,
source_graph_section(&source_graph)?.to_bytes(),
));
package.sections.push(Section::new(
SectionId::DEBUG_SOURCE_MAP,
source_map_section(&source_graph)?.to_bytes(),
));
let error_messages = error_messages_section(&source_graph);
if !error_messages.is_empty() {
package
.sections
.push(Section::new(SectionId::DEBUG_ERROR_MESSAGES, error_messages.to_bytes()));
}
}
Ok(package)
}
}
fn linked_kernel_package_section(package: &Package) -> Section {
Section::new(SectionId::KERNEL, package.to_bytes())
}
fn source_graph_section(
source_graph: &SourceDebugGraph,
) -> Result<DebugSourceGraphSection, Report> {
Ok(DebugSourceGraphSection::from_parts(
source_graph
.nodes()
.as_slice()
.iter()
.map(|source_node| {
Ok(DebugSourceNode::new(
source_node.exec_node(),
source_node
.children()
.iter()
.map(|child| DebugSourceNodeId::from(u32::from(*child)))
.collect(),
source_node.op_start().try_into().map_err(|_| {
Report::msg("source node start operation index exceeds u32")
})?,
source_node
.op_end()
.try_into()
.map_err(|_| Report::msg("source node end operation index exceeds u32"))?,
))
})
.collect::<Result<_, Report>>()?,
source_graph
.roots()
.iter()
.map(|root| DebugSourceNodeId::from(u32::from(*root)))
.collect(),
))
}
fn source_map_section(source_graph: &SourceDebugGraph) -> Result<DebugSourceMapSection, Report> {
let mut asm_ops = Vec::new();
let mut debug_vars = Vec::new();
for (source_index, source_node) in source_graph.nodes().as_slice().iter().enumerate() {
let source_node_id = DebugSourceNodeId::from(source_index as u32);
for (op_idx, asm_op) in source_node.asm_ops() {
asm_ops.push(DebugSourceAsmOp::new(
source_node_id,
(*op_idx)
.try_into()
.map_err(|_| Report::msg("source asm-op index exceeds u32"))?,
asm_op.location().cloned(),
asm_op.context_name().to_string(),
asm_op.op().to_string(),
asm_op.num_cycles(),
));
}
for (op_idx, debug_var) in source_node.debug_vars() {
debug_vars.push(DebugSourceVar::new(
source_node_id,
(*op_idx)
.try_into()
.map_err(|_| Report::msg("source debug-var index exceeds u32"))?,
debug_var.clone(),
));
}
}
Ok(DebugSourceMapSection::from_parts(asm_ops, debug_vars))
}
fn error_messages_section(source_graph: &SourceDebugGraph) -> DebugErrorMessagesSection {
DebugErrorMessagesSection::from_parts(
source_graph
.error_messages()
.iter()
.map(|(err_code, message)| DebugErrorMessage::new(*err_code, message.clone()))
.collect(),
)
}