1#[cfg(any(test, feature = "arbitrary"))]
2pub mod arbitrary;
3mod error;
4mod id;
5mod manifest;
6mod section;
7#[cfg(test)]
8mod seed_gen;
9mod serialization;
10mod target_type;
11
12use alloc::{
13 borrow::Cow,
14 boxed::Box,
15 collections::BTreeMap,
16 format,
17 string::{String, ToString},
18 sync::Arc,
19 vec::Vec,
20};
21
22use miden_assembly_syntax::{
23 Path, Report,
24 ast::{self, QualifiedProcedureName},
25 module::ModuleInfo,
26};
27#[cfg(feature = "std")]
28use miden_core::serde::DeserializationError;
29use miden_core::{
30 Word,
31 advice::AdviceMap,
32 crypto::hash::Poseidon2,
33 mast::{MastForest, MastNode, MastNodeExt, MastNodeId},
34 program::Kernel,
35 serde::{ByteReader, ByteWriter, Deserializable, Serializable, SliceReader},
36};
37
38pub use self::{
39 error::{PackageDebugInfoError, PackageStripError},
40 id::PackageId,
41 manifest::{
42 ConstantExport, ManifestValidationError, PackageExport, PackageManifest, PackageModule,
43 PackageSubmodule, ProcedureExport, TypeExport,
44 },
45 section::{InvalidSectionIdError, Section, SectionId},
46 target_type::{InvalidTargetTypeError, TargetType},
47};
48use crate::{
49 Dependency, Version,
50 debug_info::{
51 DebugFunctionInfo, DebugFunctionsSection, DebugSourceNodeId, DebugSourcesSection,
52 DebugTypeIdx, DebugTypeInfo, DebugTypesSection, PackageDebugInfo,
53 },
54};
55
56#[derive(Debug, Clone, Eq, PartialEq)]
78pub struct Package {
79 pub name: PackageId,
81 pub version: Version,
83 digest: Word,
86 pub description: Option<String>,
88 pub kind: TargetType,
90 mast: Arc<MastForest>,
92 pub manifest: PackageManifest,
95 pub sections: Vec<Section>,
98 debug_sections_trusted: bool,
105}
106
107impl Package {
109 pub fn create(
111 name: PackageId,
112 version: Version,
113 kind: TargetType,
114 mast: Arc<MastForest>,
115 exports: impl IntoIterator<Item = PackageExport>,
116 dependencies: impl IntoIterator<Item = Dependency>,
117 ) -> Result<Self, ManifestValidationError> {
118 Self::create_with_modules(name, version, kind, mast, exports, [], dependencies)
119 }
120
121 pub fn create_with_modules(
123 name: PackageId,
124 version: Version,
125 kind: TargetType,
126 mast: Arc<MastForest>,
127 exports: impl IntoIterator<Item = PackageExport>,
128 modules: impl IntoIterator<Item = PackageModule>,
129 dependencies: impl IntoIterator<Item = Dependency>,
130 ) -> Result<Self, ManifestValidationError> {
131 let manifest = PackageManifest::new(exports)?
132 .with_modules(modules)?
133 .with_dependencies(dependencies)?;
134
135 if manifest.entrypoint().is_some() && !kind.is_executable() {
136 return Err(ManifestValidationError::NonExecutableEntrypoint);
137 }
138
139 for export in manifest.exports() {
141 if let Some(proc) = export.as_procedure()
142 && let Some(node) = proc.node
143 && !mast.is_procedure_root_with_exact_digest(node, proc.digest)
144 {
145 return Err(ManifestValidationError::InvalidProcedureExport {
146 path: proc.path.clone(),
147 });
148 }
149 }
150
151 let mut package = Self {
152 name,
153 version,
154 digest: Default::default(),
155 description: None,
156 kind,
157 mast,
158 manifest,
159 sections: Vec::new(),
160 debug_sections_trusted: true,
161 };
162
163 package.compute_interface_digest()?;
164 package.recompute_mast_commitment();
165
166 Ok(package)
167 }
168
169 fn compute_interface_digest(&self) -> Result<Word, ManifestValidationError> {
170 let mut node_ids = Vec::with_capacity(self.manifest.num_exports());
171 for export in self.manifest.exports() {
172 if let PackageExport::Procedure(export) = export {
173 if let Some(node_id) = export.node {
174 node_ids.push(node_id);
175 } else {
176 node_ids.push(self.mast.find_procedure_root(export.digest).ok_or_else(
177 || ManifestValidationError::MissingProcedureMast {
178 path: export.path.clone(),
179 digest: export.digest,
180 },
181 )?);
182 }
183 }
184 }
185
186 Ok(self.mast.compute_nodes_commitment(node_ids.iter()))
187 }
188
189 fn recompute_mast_commitment(&mut self) {
190 self.digest = self.mast.commitment();
191 }
192
193 pub fn with_advice_map(mut self, advice_map: AdviceMap) -> Self {
196 self.extend_advice_map(advice_map);
197 self
198 }
199
200 pub fn extend_advice_map(&mut self, advice_map: AdviceMap) {
202 self.mast = Arc::new(self.mast.as_ref().clone().with_advice_map(advice_map));
203 self.recompute_mast_commitment();
204 }
205
206 pub fn strip_debug_info(&mut self) -> Result<(), PackageStripError> {
211 for section in self.sections.iter_mut().filter(|section| section.id == SectionId::KERNEL) {
212 let mut kernel_package = Self::read_from_bytes(section.data.as_ref())
213 .map_err(|source| PackageStripError::DecodeEmbeddedKernel { source })?;
214 kernel_package.strip_debug_info()?;
215 section.data = Cow::Owned(kernel_package.to_bytes());
216 }
217
218 self.sections.retain(|section| !section.id.is_debug());
219 Ok(())
220 }
221
222 pub fn without_debug_info(mut self) -> Result<Self, PackageStripError> {
224 self.strip_debug_info()?;
225 Ok(self)
226 }
227}
228
229impl Package {
231 pub const EXTENSION: &str = "masp";
233
234 #[inline]
236 pub fn mast_forest(&self) -> &Arc<MastForest> {
237 &self.mast
238 }
239
240 #[inline]
242 pub fn digest(&self) -> Word {
243 self.digest
244 }
245
246 pub fn interface_digest(&self) -> Result<Word, ManifestValidationError> {
248 self.compute_interface_digest()
249 }
250
251 pub fn content_digest(&self) -> Word {
259 let mut bytes = Vec::new();
260 self.write_content_digest_preimage(&mut bytes, None);
261 Poseidon2::hash(&bytes)
262 }
263
264 fn write_content_digest_preimage<W: ByteWriter>(
265 &self,
266 target: &mut W,
267 kernel_digest: Option<&Word>,
268 ) {
269 target.write_bytes(b"miden.package.content.v2");
270 self.digest().write_into(target);
271 self.name.write_into(target);
272 self.version.to_string().write_into(target);
273 target.write_u8(self.kind.into());
274 self.manifest.write_into(target);
275 self.write_content_digest_sections(target);
276 target.write_bool(kernel_digest.is_some());
277 if let Some(kernel_digest) = kernel_digest {
278 kernel_digest.write_into(target);
279 }
280 }
281
282 fn write_content_digest_sections<W: ByteWriter>(&self, target: &mut W) {
283 let semantic_sections = self
284 .sections
285 .iter()
286 .filter(|section| section.id == SectionId::ACCOUNT_COMPONENT_METADATA)
287 .collect::<Vec<_>>();
288 target.write_usize(semantic_sections.len());
289 for section in semantic_sections {
290 section.write_into(target);
291 }
292 }
293
294 pub fn is_program(&self) -> bool {
296 self.kind.is_executable()
297 }
298
299 pub fn is_library(&self) -> bool {
301 self.kind.is_library()
302 }
303
304 pub fn is_kernel(&self) -> bool {
306 matches!(self.kind, TargetType::Kernel)
307 }
308
309 #[inline]
311 pub fn entrypoint(&self) -> Option<Arc<Path>> {
312 self.manifest.entrypoint()
313 }
314
315 #[inline]
317 pub fn entrypoint_source_node(&self) -> Option<DebugSourceNodeId> {
318 self.entrypoint()
319 .as_deref()
320 .and_then(|entrypoint| self.get_export_by_lookup_path(entrypoint))
321 .and_then(PackageExport::as_procedure)
322 .and_then(|procedure| procedure.source_node)
323 }
324
325 pub fn kernel_module_info(&self) -> Result<ModuleInfo, Report> {
327 self.try_module_infos()
328 .map_err(Report::msg)?
329 .into_iter()
330 .find(|mi| mi.path().is_kernel_path())
331 .ok_or_else(|| Report::msg("invalid kernel package: does not contain kernel module"))
332 }
333
334 pub fn kernel_runtime_dependency(&self) -> Result<Option<&Dependency>, Report> {
339 let mut kernel_dependencies = self
340 .manifest
341 .dependencies()
342 .filter(|dependency| dependency.kind == TargetType::Kernel);
343 let Some(kernel_dependency) = kernel_dependencies.next() else {
344 return Ok(None);
345 };
346 if kernel_dependencies.next().is_some() {
347 return Err(Report::msg(format!(
348 "package '{}' declares multiple kernel runtime dependencies",
349 self.name
350 )));
351 }
352
353 Ok(Some(kernel_dependency))
354 }
355
356 pub fn debug_info(&self) -> Result<Option<PackageDebugInfo>, PackageDebugInfoError> {
366 if !self.debug_sections_trusted && self.sections.iter().any(|section| section.id.is_debug())
367 {
368 return Err(PackageDebugInfoError::UntrustedSections);
369 }
370
371 let debug_info = PackageDebugInfo {
372 types: self.read_debug_section(SectionId::DEBUG_TYPES)?,
373 sources: self.read_debug_section(SectionId::DEBUG_SOURCES)?,
374 functions: self.read_debug_section(SectionId::DEBUG_FUNCTIONS)?,
375 source_graph: self.read_debug_section(SectionId::DEBUG_SOURCE_GRAPH)?,
376 source_map: self.read_debug_section(SectionId::DEBUG_SOURCE_MAP)?,
377 error_messages: self.read_debug_section(SectionId::DEBUG_ERROR_MESSAGES)?,
378 };
379
380 if debug_info.is_empty() {
381 return Ok(None);
382 }
383
384 self.validate_debug_info(&debug_info)?;
385 Ok(Some(debug_info))
386 }
387
388 pub fn get_export_node_id(&self, path: impl AsRef<Path>) -> MastNodeId {
394 self.get_export_by_lookup_path(path.as_ref())
395 .and_then(PackageExport::as_procedure)
396 .and_then(|export| export.node.or_else(|| self.mast.find_procedure_root(export.digest)))
397 .expect("procedure not exported from this package")
398 }
399
400 pub fn is_reexport(&self, path: impl AsRef<Path>) -> bool {
402 self.get_export_by_lookup_path(path.as_ref())
403 .and_then(PackageExport::as_procedure)
404 .and_then(|export| export.node.or_else(|| self.mast.find_procedure_root(export.digest)))
405 .map(|node| self.mast[node].is_external())
406 .unwrap_or(false)
407 }
408
409 pub fn get_procedure_root_by_path(&self, path: impl AsRef<Path>) -> Option<Word> {
412 self.get_export_by_lookup_path(path.as_ref())
413 .and_then(PackageExport::as_procedure)
414 .map(|proc| proc.digest)
415 }
416
417 pub fn get_procedure_node_by_path(&self, path: impl AsRef<Path>) -> Option<MastNodeId> {
419 self.get_export_by_lookup_path(path.as_ref())
420 .and_then(PackageExport::as_procedure)
421 .and_then(|export| export.node.or_else(|| self.mast.find_procedure_root(export.digest)))
422 }
423
424 fn get_export_by_lookup_path(&self, path: &Path) -> Option<&PackageExport> {
425 self.manifest
426 .get_export(path)
427 .or_else(|| path.is_absolute().then(|| self.manifest.get_export(path.to_relative()))?)
428 .or_else(|| {
429 if path.is_absolute() {
430 None
431 } else {
432 path.to_absolute().ok().and_then(|path| self.manifest.get_export(path.as_ref()))
433 }
434 })
435 }
436
437 pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
439 let source_library_commitment =
440 self.interface_digest().expect("package manifest exports were validated");
441 let mut modules_by_path: BTreeMap<Arc<Path>, ModuleInfo> = BTreeMap::new();
442
443 for module in self.manifest.modules() {
444 let mut module_info = ModuleInfo::new(module.path.clone(), None);
445 for submodule in module.submodules() {
446 module_info.add_submodule(ast::SubmoduleDecl {
447 visibility: ast::Visibility::Public,
448 name: submodule.name.clone(),
449 });
450 }
451 modules_by_path.insert(module.path.clone(), module_info);
452 }
453
454 for export in self.manifest.exports() {
455 let module_name =
456 Arc::from(export.path().parent().unwrap().to_path_buf().into_boxed_path());
457 let module = modules_by_path
458 .entry(Arc::clone(&module_name))
459 .or_insert_with(|| ModuleInfo::new(module_name, None));
460 match export {
461 PackageExport::Procedure(ProcedureExport {
462 node,
463 source_node,
464 digest,
465 path,
466 signature,
467 attributes,
468 }) => {
469 let name = path.last().unwrap();
470 module.add_procedure_with_provenance(
471 ast::ProcedureName::new(name).expect("valid procedure name"),
472 *digest,
473 signature.clone().map(Arc::new),
474 attributes.clone(),
475 *node,
476 source_node.map(u32::from),
477 Some(source_library_commitment),
478 );
479 },
480 PackageExport::Constant(ConstantExport { path, value }) => {
481 let name = ast::Ident::new(path.last().unwrap()).expect("valid identifier");
482 module.add_constant(name, value.clone());
483 },
484 PackageExport::Type(TypeExport { path, ty }) => {
485 let name = ast::Ident::new(path.last().unwrap()).expect("valid identifier");
486 module.add_type(name, ty.clone());
487 },
488 }
489 }
490
491 modules_by_path.into_values()
492 }
493
494 pub fn try_module_infos(&self) -> Result<Vec<ModuleInfo>, ManifestValidationError> {
500 let source_library_commitment = self.interface_digest()?;
501 let mut modules_by_path: BTreeMap<Arc<Path>, ModuleInfo> = BTreeMap::new();
502
503 for module in self.manifest.modules() {
504 let mut module_info = ModuleInfo::new(module.path.clone(), None);
505 for submodule in module.submodules() {
506 module_info.add_submodule(ast::SubmoduleDecl {
507 visibility: ast::Visibility::Public,
508 name: submodule.name.clone(),
509 });
510 }
511 modules_by_path.insert(module.path.clone(), module_info);
512 }
513
514 for module in self.manifest.modules() {
515 for submodule in module.submodules() {
516 let child_path: Arc<Path> =
517 Arc::from(module.path.join(&submodule.name).into_boxed_path());
518 if !modules_by_path.contains_key(child_path.as_ref()) {
519 return Err(ManifestValidationError::MissingDeclaredSubmoduleSurface {
520 parent: module.path.clone(),
521 name: submodule.name.to_string(),
522 module: child_path,
523 });
524 }
525 }
526 }
527
528 for module in self.manifest.modules() {
529 let Some(parent_path) = module.path.parent() else {
530 continue;
531 };
532 let parent_path: Arc<Path> = Arc::from(parent_path.to_path_buf().into_boxed_path());
533 let Some(parent) = self.manifest.get_module(parent_path.as_ref()) else {
534 continue;
535 };
536 let name = module.path.last().expect("module paths have at least one component");
537 if !parent.submodules().iter().any(|submodule| submodule.name.as_str() == name) {
538 return Err(ManifestValidationError::UndeclaredModuleSurface {
539 module: module.path.clone(),
540 parent: parent.path.clone(),
541 name: name.to_string(),
542 });
543 }
544 }
545
546 for export in self.manifest.exports() {
547 let module_name: Arc<Path> =
548 Arc::from(export.path().parent().unwrap().to_path_buf().into_boxed_path());
549 let module = modules_by_path.get_mut(module_name.as_ref()).ok_or_else(|| {
550 ManifestValidationError::MissingExportModuleSurface {
551 export: export.path(),
552 module: module_name.clone(),
553 }
554 })?;
555 match export {
556 PackageExport::Procedure(ProcedureExport {
557 node,
558 source_node,
559 digest,
560 path,
561 signature,
562 attributes,
563 }) => {
564 let name = path.last().unwrap();
565 module.add_procedure_with_provenance(
566 ast::ProcedureName::new(name).expect("valid procedure name"),
567 *digest,
568 signature.clone().map(Arc::new),
569 attributes.clone(),
570 *node,
571 source_node.map(u32::from),
572 Some(source_library_commitment),
573 );
574 },
575 PackageExport::Constant(ConstantExport { path, value }) => {
576 let name = ast::Ident::new(path.last().unwrap()).expect("valid identifier");
577 module.add_constant(name, value.clone());
578 },
579 PackageExport::Type(TypeExport { path, ty }) => {
580 let name = ast::Ident::new(path.last().unwrap()).expect("valid identifier");
581 module.add_type(name, ty.clone());
582 },
583 }
584 }
585
586 Ok(modules_by_path.into_values().collect())
587 }
588
589 fn read_debug_section<T>(&self, id: SectionId) -> Result<Option<T>, PackageDebugInfoError>
590 where
591 T: Deserializable,
592 {
593 let mut sections = self.sections.iter().filter(|section| section.id == id);
594 let Some(section) = sections.next() else {
595 return Ok(None);
596 };
597 if sections.next().is_some() {
598 return Err(PackageDebugInfoError::DuplicateSection { id });
599 }
600
601 read_section_payload(&id, section.data.as_ref()).map(Some)
602 }
603
604 fn validate_debug_info(
605 &self,
606 debug_info: &PackageDebugInfo,
607 ) -> Result<(), PackageDebugInfoError> {
608 if let Some(types) = debug_info.types.as_ref() {
609 self.validate_debug_types(types)?;
610 }
611 if let Some(sources) = debug_info.sources.as_ref() {
612 self.validate_debug_sources(sources)?;
613 }
614 if let Some(functions) = debug_info.functions.as_ref() {
615 self.validate_debug_functions(
616 debug_info.types.as_ref(),
617 debug_info.sources.as_ref(),
618 functions,
619 )?;
620 }
621
622 let source_graph = debug_info.source_graph.as_ref();
623 if let Some(source_map) = debug_info.source_map.as_ref()
624 && source_graph.is_none()
625 && !source_map.is_empty()
626 {
627 return Err(PackageDebugInfoError::InvalidReference {
628 message: "debug source map is present without a debug source graph".to_string(),
629 });
630 }
631
632 let Some(source_graph) = source_graph else {
633 return Ok(());
634 };
635
636 for root in source_graph.roots().iter().copied() {
637 if debug_info.source_node(root).is_none() {
638 return Err(PackageDebugInfoError::InvalidReference {
639 message: format!("debug source root {root:?} is not present in the graph"),
640 });
641 }
642 }
643
644 for (source_index, source_node) in source_graph.nodes().iter().enumerate() {
645 let source_id = DebugSourceNodeId::from(source_index as u32);
646 let Some(exec_node) = self.mast.get_node_by_id(source_node.exec_node) else {
647 return Err(PackageDebugInfoError::InvalidReference {
648 message: format!(
649 "debug source node {source_id:?} references missing execution node {:?}",
650 source_node.exec_node,
651 ),
652 });
653 };
654 if source_node.op_start > source_node.op_end {
655 return Err(PackageDebugInfoError::InvalidReference {
656 message: format!(
657 "debug source node {source_id:?} has invalid operation range {}..{}",
658 source_node.op_start, source_node.op_end,
659 ),
660 });
661 }
662 if let MastNode::Block(block) = exec_node {
663 let num_ops = block.num_operations();
664 if source_node.op_end > num_ops {
665 return Err(PackageDebugInfoError::InvalidReference {
666 message: format!(
667 "debug source node {source_id:?} has operation range {}..{}, outside execution node {:?} operation count {num_ops}",
668 source_node.op_start, source_node.op_end, source_node.exec_node,
669 ),
670 });
671 }
672 }
673
674 let mut exec_children = Vec::new();
675 exec_node.for_each_child(|child_id| exec_children.push(child_id));
676 if exec_children.len() != source_node.children.len() {
677 return Err(PackageDebugInfoError::InvalidReference {
678 message: format!(
679 "debug source node {source_id:?} has {} children, expected {} from execution node {:?}",
680 source_node.children.len(),
681 exec_children.len(),
682 source_node.exec_node,
683 ),
684 });
685 }
686
687 for (child_index, child_source_id) in source_node.children.iter().copied().enumerate() {
688 let Some(child_source_node) = debug_info.source_node(child_source_id) else {
689 return Err(PackageDebugInfoError::InvalidReference {
690 message: format!(
691 "debug source node {source_id:?} references missing child source node {child_source_id:?}",
692 ),
693 });
694 };
695 if child_source_node.exec_node != exec_children[child_index] {
696 return Err(PackageDebugInfoError::InvalidReference {
697 message: format!(
698 "debug source node {source_id:?} child {child_index} maps to {:?}, expected {:?}",
699 child_source_node.exec_node, exec_children[child_index],
700 ),
701 });
702 }
703 }
704 }
705
706 if let Some(source_map) = debug_info.source_map.as_ref() {
707 for row in source_map.asm_ops() {
708 self.validate_source_map_row(
709 source_graph,
710 row.source_node,
711 row.op_idx,
712 "assembly op",
713 )?;
714 }
715 for row in source_map.debug_vars() {
716 self.validate_source_map_row(
717 source_graph,
718 row.source_node,
719 row.op_idx,
720 "debug variable",
721 )?;
722 }
723 let function_count =
724 debug_info.functions.as_ref().map_or(0, |section| section.functions.len());
725 let file_count = debug_info.sources.as_ref().map_or(0, |section| section.files.len());
726 for row in source_map.inline_calls() {
727 self.validate_source_map_row(
728 source_graph,
729 row.source_node,
730 row.op_idx,
731 "inline call",
732 )?;
733 if row.callee_idx as usize >= function_count {
734 return Err(PackageDebugInfoError::InvalidReference {
735 message: format!(
736 "debug inline call callee index {} is outside debug function table length {function_count}",
737 row.callee_idx,
738 ),
739 });
740 }
741 if row.file_idx as usize >= file_count {
742 return Err(PackageDebugInfoError::InvalidReference {
743 message: format!(
744 "debug inline call file index {} is outside debug source file table length {file_count}",
745 row.file_idx,
746 ),
747 });
748 }
749 }
750 }
751
752 for export in self.manifest.exports() {
753 let Some(procedure) = export.as_procedure() else {
754 continue;
755 };
756 let Some(source_node_id) = procedure.source_node else {
757 continue;
758 };
759 let Some(source_node) = debug_info.source_node(source_node_id) else {
760 return Err(PackageDebugInfoError::InvalidReference {
761 message: format!(
762 "procedure export '{}' references missing source node {source_node_id:?}",
763 procedure.path,
764 ),
765 });
766 };
767 let Some(export_node) =
768 procedure.node.or_else(|| self.mast.find_procedure_root(procedure.digest))
769 else {
770 return Err(PackageDebugInfoError::InvalidReference {
771 message: format!(
772 "procedure export '{}' does not resolve to an execution node",
773 procedure.path,
774 ),
775 });
776 };
777 if source_node.exec_node != export_node {
778 return Err(PackageDebugInfoError::InvalidReference {
779 message: format!(
780 "procedure export '{}' source node {source_node_id:?} maps to {:?}, expected {export_node:?}",
781 procedure.path, source_node.exec_node,
782 ),
783 });
784 }
785 }
786
787 Ok(())
788 }
789
790 fn validate_debug_types(&self, types: &DebugTypesSection) -> Result<(), PackageDebugInfoError> {
791 let type_count = types.types.len();
792 let string_count = types.strings.len();
793 for (type_index, ty) in types.types.iter().enumerate() {
794 self.validate_debug_type(ty, type_index, type_count, string_count)?;
795 }
796 Ok(())
797 }
798
799 fn validate_debug_type(
800 &self,
801 ty: &DebugTypeInfo,
802 type_index: usize,
803 type_count: usize,
804 string_count: usize,
805 ) -> Result<(), PackageDebugInfoError> {
806 match ty {
807 DebugTypeInfo::Primitive(_) | DebugTypeInfo::Unknown => Ok(()),
808 DebugTypeInfo::Pointer { pointee_type_idx } => self.validate_type_index(
809 *pointee_type_idx,
810 type_count,
811 format!("debug type {type_index} pointer target"),
812 ),
813 DebugTypeInfo::Array { element_type_idx, .. } => self.validate_type_index(
814 *element_type_idx,
815 type_count,
816 format!("debug type {type_index} array element"),
817 ),
818 DebugTypeInfo::Struct { name_idx, fields, .. } => {
819 self.validate_string_index(
820 *name_idx,
821 string_count,
822 format!("debug type {type_index} struct name"),
823 )?;
824 for (field_index, field) in fields.iter().enumerate() {
825 self.validate_string_index(
826 field.name_idx,
827 string_count,
828 format!("debug type {type_index} field {field_index} name"),
829 )?;
830 self.validate_type_index(
831 field.type_idx,
832 type_count,
833 format!("debug type {type_index} field {field_index} type"),
834 )?;
835 }
836 Ok(())
837 },
838 DebugTypeInfo::Function { return_type_idx, param_type_indices } => {
839 if let Some(return_type_idx) = return_type_idx {
840 self.validate_type_index(
841 *return_type_idx,
842 type_count,
843 format!("debug type {type_index} function return type"),
844 )?;
845 }
846 for (param_index, param_type_idx) in param_type_indices.iter().copied().enumerate()
847 {
848 self.validate_type_index(
849 param_type_idx,
850 type_count,
851 format!("debug type {type_index} function parameter {param_index}"),
852 )?;
853 }
854 Ok(())
855 },
856 DebugTypeInfo::Enum {
857 name_idx,
858 discriminant_type_idx,
859 variants,
860 ..
861 } => {
862 self.validate_string_index(
863 *name_idx,
864 string_count,
865 format!("debug type {type_index} enum name"),
866 )?;
867 self.validate_type_index(
868 *discriminant_type_idx,
869 type_count,
870 format!("debug type {type_index} enum discriminant"),
871 )?;
872 for (variant_index, variant) in variants.iter().enumerate() {
873 self.validate_string_index(
874 variant.name_idx,
875 string_count,
876 format!("debug type {type_index} variant {variant_index} name"),
877 )?;
878 if let Some(type_idx) = variant.type_idx {
879 self.validate_type_index(
880 type_idx,
881 type_count,
882 format!("debug type {type_index} variant {variant_index} payload"),
883 )?;
884 }
885 }
886 Ok(())
887 },
888 }
889 }
890
891 fn validate_debug_sources(
892 &self,
893 sources: &DebugSourcesSection,
894 ) -> Result<(), PackageDebugInfoError> {
895 let string_count = sources.strings.len();
896 for (file_index, file) in sources.files.iter().enumerate() {
897 self.validate_string_index(
898 file.path_idx,
899 string_count,
900 format!("debug source file {file_index} path"),
901 )?;
902 }
903 Ok(())
904 }
905
906 fn validate_debug_functions(
907 &self,
908 types: Option<&DebugTypesSection>,
909 sources: Option<&DebugSourcesSection>,
910 functions: &DebugFunctionsSection,
911 ) -> Result<(), PackageDebugInfoError> {
912 let string_count = functions.strings.len();
913 let file_count = sources.map_or(0, |sources| sources.files.len());
914 let type_count = types.map_or(0, |types| types.types.len());
915
916 for (function_index, function) in functions.functions.iter().enumerate() {
917 self.validate_debug_function(
918 function,
919 function_index,
920 string_count,
921 file_count,
922 type_count,
923 )?;
924 }
925 Ok(())
926 }
927
928 fn validate_debug_function(
929 &self,
930 function: &DebugFunctionInfo,
931 function_index: usize,
932 string_count: usize,
933 file_count: usize,
934 type_count: usize,
935 ) -> Result<(), PackageDebugInfoError> {
936 self.validate_string_index(
937 function.name_idx,
938 string_count,
939 format!("debug function {function_index} name"),
940 )?;
941 if let Some(linkage_name_idx) = function.linkage_name_idx {
942 self.validate_string_index(
943 linkage_name_idx,
944 string_count,
945 format!("debug function {function_index} linkage name"),
946 )?;
947 }
948 if function.file_idx as usize >= file_count {
949 return Err(PackageDebugInfoError::InvalidReference {
950 message: format!(
951 "debug function {function_index} file index {} is outside debug source file table length {file_count}",
952 function.file_idx,
953 ),
954 });
955 }
956 if let Some(type_idx) = function.type_idx {
957 self.validate_type_index(
958 type_idx,
959 type_count,
960 format!("debug function {function_index} type"),
961 )?;
962 }
963 Ok(())
964 }
965
966 fn validate_string_index(
967 &self,
968 index: u32,
969 string_count: usize,
970 context: String,
971 ) -> Result<(), PackageDebugInfoError> {
972 if index as usize >= string_count {
973 return Err(PackageDebugInfoError::InvalidReference {
974 message: format!(
975 "{context} string index {index} is outside string table length {string_count}",
976 ),
977 });
978 }
979 Ok(())
980 }
981
982 fn validate_type_index(
983 &self,
984 index: DebugTypeIdx,
985 type_count: usize,
986 context: String,
987 ) -> Result<(), PackageDebugInfoError> {
988 if index.as_u32() as usize >= type_count {
989 return Err(PackageDebugInfoError::InvalidReference {
990 message: format!(
991 "{context} type index {} is outside type table length {type_count}",
992 index.as_u32(),
993 ),
994 });
995 }
996 Ok(())
997 }
998
999 fn validate_source_map_row(
1000 &self,
1001 source_graph: &crate::debug_info::DebugSourceGraphSection,
1002 source_node_id: DebugSourceNodeId,
1003 op_idx: u32,
1004 row_kind: &'static str,
1005 ) -> Result<(), PackageDebugInfoError> {
1006 let Some(source_node) = source_graph.nodes().get(source_node_id.as_u32() as usize) else {
1007 return Err(PackageDebugInfoError::InvalidReference {
1008 message: format!(
1009 "{row_kind} row references missing source node {source_node_id:?}"
1010 ),
1011 });
1012 };
1013 if op_idx < source_node.op_start || op_idx >= source_node.op_end {
1014 return Err(PackageDebugInfoError::InvalidReference {
1015 message: format!(
1016 "{row_kind} row for source node {source_node_id:?} has op index {op_idx}, outside source range {}..{}",
1017 source_node.op_start, source_node.op_end,
1018 ),
1019 });
1020 }
1021 Ok(())
1022 }
1023}
1024
1025fn read_section_payload<T>(id: &SectionId, bytes: &[u8]) -> Result<T, PackageDebugInfoError>
1026where
1027 T: Deserializable,
1028{
1029 let mut reader = SliceReader::new(bytes);
1030 let section = T::read_from(&mut reader)
1031 .map_err(|source| PackageDebugInfoError::DecodeSection { id: id.clone(), source })?;
1032 if reader.has_more_bytes() {
1033 return Err(PackageDebugInfoError::TrailingBytes { id: id.clone() });
1034 }
1035 Ok(section)
1036}
1037
1038impl Package {
1040 pub fn to_kernel(&self) -> Result<Kernel, Report> {
1042 let exports = self
1043 .manifest
1044 .exports()
1045 .filter_map(|export| {
1046 if export.namespace().is_kernel_path()
1047 && let PackageExport::Procedure(p) = export
1048 {
1049 Some(p.digest)
1050 } else {
1051 None
1052 }
1053 })
1054 .collect::<Vec<_>>();
1055 if exports.is_empty() {
1056 return Err(Report::msg(
1057 "invalid kernel package: does not export any kernel procedures",
1058 ));
1059 }
1060 Kernel::new(&exports).map_err(|err| Report::msg(format!("invalid kernel package: {err}")))
1061 }
1062
1063 #[doc(hidden)]
1065 pub fn try_into_program(&self) -> Result<miden_core::program::Program, Report> {
1066 use miden_assembly_syntax::{Path as MasmPath, ast};
1067 use miden_core::program::Program;
1068
1069 if !self.is_program() {
1070 return Err(Report::msg(format!(
1071 "cannot convert package of type {} to Executable",
1072 self.kind
1073 )));
1074 }
1075 let entrypoint = self.manifest.entrypoint().unwrap_or_else(|| {
1076 MasmPath::exec_path().join(ast::ProcedureName::MAIN_PROC_NAME).into()
1077 });
1078 if let Some(entrypoint) = self.get_procedure_node_by_path(&entrypoint) {
1079 let mast_forest = self.mast.clone();
1080 let kernel_dependency = self.kernel_runtime_dependency()?.cloned();
1081 match (self.try_embedded_kernel_package()?, kernel_dependency) {
1082 (Some(kernel_package), _) => {
1083 Ok(Program::with_kernel(mast_forest, entrypoint, kernel_package.to_kernel()?))
1084 },
1085 (None, Some(kernel_dependency)) => Err(Report::msg(format!(
1086 "package '{}' declares kernel runtime dependency '{}@{}#{}', but does not embed the kernel package required to reconstruct a program",
1087 self.name,
1088 kernel_dependency.name,
1089 kernel_dependency.version,
1090 kernel_dependency.digest
1091 ))),
1092 (None, None) => Ok(Program::new(mast_forest, entrypoint)),
1093 }
1094 } else {
1095 Err(Report::msg(format!(
1096 "malformed executable package: no procedure root for '{entrypoint}'"
1097 )))
1098 }
1099 }
1100
1101 #[doc(hidden)]
1103 pub fn unwrap_program(&self) -> miden_core::program::Program {
1104 assert_eq!(self.kind, TargetType::Executable);
1105 self.try_into_program().unwrap_or_else(|err| panic!("{err}"))
1106 }
1107
1108 pub fn try_embedded_kernel_package(&self) -> Result<Option<Box<Self>>, Report> {
1118 let Some(kernel_package) = self.embedded_kernel_package()? else {
1119 return Ok(None);
1120 };
1121 self.validate_embedded_kernel_dependency(&kernel_package)?;
1122 Ok(Some(kernel_package))
1123 }
1124
1125 fn embedded_kernel_package(&self) -> Result<Option<Box<Self>>, Report> {
1133 let mut sections = self.sections.iter().filter(|section| section.id == SectionId::KERNEL);
1134 let Some(section) = sections.next() else {
1135 return Ok(None);
1136 };
1137 if sections.next().is_some() {
1138 return Err(Report::msg(format!(
1139 "package '{}' contains multiple '{}' sections",
1140 self.name,
1141 SectionId::KERNEL
1142 )));
1143 }
1144
1145 Self::read_from_bytes(section.data.as_ref())
1146 .map(Box::new)
1147 .map(Some)
1148 .map_err(|error| {
1149 Report::msg(format!(
1150 "failed to decode embedded kernel package for '{}': {error}",
1151 self.name
1152 ))
1153 })
1154 }
1155
1156 fn validate_embedded_kernel_dependency(&self, kernel_package: &Self) -> Result<(), Report> {
1157 if !kernel_package.is_kernel() {
1158 return Err(Report::msg(format!(
1159 "package '{}' embeds '{}', but its kind is '{}'",
1160 self.name, kernel_package.name, kernel_package.kind
1161 )));
1162 }
1163
1164 let Some(kernel_dependency) = self.kernel_runtime_dependency()? else {
1165 return Err(Report::msg(format!(
1166 "package '{}' embeds a kernel package, but does not declare a kernel runtime dependency",
1167 self.name
1168 )));
1169 };
1170
1171 if kernel_dependency.name != kernel_package.name
1172 || kernel_dependency.version != kernel_package.version
1173 || kernel_dependency.digest != kernel_package.digest()
1174 {
1175 return Err(Report::msg(format!(
1176 "package '{}' declares kernel runtime dependency '{}@{}#{}', but that does not match the embedded kernel package '{}@{}#{}'",
1177 self.name,
1178 kernel_dependency.name,
1179 kernel_dependency.version,
1180 kernel_dependency.digest,
1181 kernel_package.name,
1182 kernel_package.version,
1183 kernel_package.digest()
1184 )));
1185 }
1186
1187 Ok(())
1188 }
1189
1190 pub fn to_dependency(&self) -> Dependency {
1192 Dependency {
1193 name: self.name.clone(),
1194 version: self.version.clone(),
1195 kind: self.kind,
1196 digest: self.digest(),
1197 }
1198 }
1199
1200 pub fn make_executable(&self, entrypoint: &QualifiedProcedureName) -> Result<Self, Report> {
1213 use miden_assembly_syntax::Path as MasmPath;
1214 if !self.is_library() {
1215 return Err(Report::msg("expected library but got an executable"));
1216 }
1217
1218 let entrypoint =
1219 Arc::<MasmPath>::from(entrypoint.to_absolute().map_err(Report::msg)?.to_path_buf());
1220 if let Some(export) = self.get_export_by_lookup_path(&entrypoint) {
1221 match export {
1222 PackageExport::Constant(_) | PackageExport::Type(_) => {
1223 let actual = match export {
1224 PackageExport::Constant(_) => "constant",
1225 PackageExport::Type(_) => "type",
1226 _ => unreachable!(),
1227 };
1228 Err(Report::msg(ManifestValidationError::UnexpectedExportType {
1229 path: entrypoint,
1230 expected: "procedure",
1231 actual,
1232 }))
1233 },
1234 PackageExport::Procedure(procedure) => {
1235 let executable_entrypoint: Arc<MasmPath> =
1236 MasmPath::exec_path().join(ast::ProcedureName::MAIN_PROC_NAME).into();
1237 let mut procedure = procedure.clone();
1238 procedure.path = executable_entrypoint;
1239 let mut package = Self::create(
1240 self.name.clone(),
1241 self.version.clone(),
1242 TargetType::Executable,
1243 self.mast.clone(),
1244 [PackageExport::Procedure(procedure)],
1245 self.manifest.dependencies.clone(),
1246 )
1247 .map_err(Report::msg)?;
1248 package.description = self.description.clone();
1249 package.sections = self.sections.clone();
1250 package.debug_sections_trusted = self.debug_sections_trusted;
1251 Ok(package)
1252 },
1253 }
1254 } else {
1255 Err(Report::msg(format!(
1256 "invalid entrypoint: library does not export '{entrypoint}'"
1257 )))
1258 }
1259 }
1260}
1261
1262impl Package {
1264 #[cfg(feature = "std")]
1266 pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
1267 use miden_core::serde::Serializable;
1268
1269 let path = path.as_ref();
1270 if let Some(dir) = path.parent() {
1271 std::fs::create_dir_all(dir)?;
1272 }
1273
1274 let mut file = std::fs::File::create(path)?;
1275 <Self as Serializable>::write_into(self, &mut file);
1276 Ok(())
1277 }
1278
1279 #[cfg(feature = "std")]
1281 pub fn write_masp_file(&self, dir: impl AsRef<std::path::Path>) -> std::io::Result<()> {
1282 let dir = dir.as_ref();
1283 let package_name: &str = &self.name;
1284 self.write_to_file(dir.join(package_name).with_extension(Self::EXTENSION))
1285 .map_err(|err| std::io::Error::other(err.to_string()))
1286 }
1287
1288 #[cfg(feature = "std")]
1289 pub fn deserialize_from_file(
1295 path: impl AsRef<std::path::Path>,
1296 ) -> Result<Self, DeserializationError> {
1297 let bytes = read_package_file(path)?;
1298 Self::read_from_bytes(&bytes)
1299 }
1300
1301 #[cfg(feature = "std")]
1302 pub fn deserialize_from_file_trusted(
1308 path: impl AsRef<std::path::Path>,
1309 ) -> Result<Self, DeserializationError> {
1310 let bytes = read_package_file(path)?;
1311 Self::read_from_bytes_trusted(&bytes)
1312 }
1313}
1314
1315#[cfg(feature = "std")]
1316fn read_package_file(path: impl AsRef<std::path::Path>) -> Result<Vec<u8>, DeserializationError> {
1317 let path = path.as_ref();
1318 std::fs::read(path).map_err(|err| {
1319 DeserializationError::InvalidValue(format!(
1320 "failed to open file at {}: {err}",
1321 path.to_string_lossy()
1322 ))
1323 })
1324}
1325
1326#[cfg(test)]
1330mod tests {
1331 use alloc::{sync::Arc, vec, vec::Vec};
1332 use core::{assert_matches, str::FromStr};
1333
1334 use miden_assembly_syntax::ast::{
1335 Path as AstPath, PathBuf, ProcedureName, QualifiedProcedureName,
1336 };
1337 use miden_core::{
1338 Felt, Word,
1339 advice::AdviceMap,
1340 mast::{
1341 BasicBlockNodeBuilder, DenseMastForestBuilder, ExternalNodeBuilder, MastForest,
1342 MastNode, MastNodeExt, MastNodeId, SplitNodeBuilder,
1343 },
1344 operations::Operation,
1345 serde::Serializable,
1346 utils::IndexVec,
1347 };
1348 use miden_debug_types::{ColumnNumber, LineNumber};
1349
1350 use super::*;
1351 use crate::{
1352 Dependency, Version,
1353 debug_info::{
1354 DebugFileInfo, DebugFunctionInfo, DebugFunctionsSection, DebugSourceAsmOp,
1355 DebugSourceGraphSection, DebugSourceInlineCall, DebugSourceMapSection, DebugSourceNode,
1356 DebugSourceNodeId, DebugSourcesSection, DebugTypeIdx, DebugTypeInfo, DebugTypesSection,
1357 },
1358 };
1359
1360 fn build_forest() -> (MastForest, MastNodeId) {
1361 let mut builder = DenseMastForestBuilder::new();
1362 let node_id = builder
1363 .push_node(BasicBlockNodeBuilder::new(vec![Operation::Add]))
1364 .expect("failed to build basic block");
1365 builder.mark_root(node_id);
1366 let (forest, remapping) = builder.finish_with_id_map().expect("failed to build forest");
1367 let node_id = remapping.get(node_id).expect("root node should be retained");
1368 (forest, node_id)
1369 }
1370
1371 fn build_split_forest() -> (MastForest, MastNodeId, MastNodeId, MastNodeId) {
1372 let mut builder = DenseMastForestBuilder::new();
1373 let left_id = builder
1374 .push_node(BasicBlockNodeBuilder::new(vec![Operation::Add]))
1375 .expect("failed to build left basic block");
1376 let right_id = builder
1377 .push_node(BasicBlockNodeBuilder::new(vec![Operation::Mul]))
1378 .expect("failed to build right basic block");
1379 let root_id = builder
1380 .push_node(SplitNodeBuilder::new([left_id, right_id]))
1381 .expect("failed to build split node");
1382 builder.mark_root(root_id);
1383 let (forest, remapping) = builder.finish_with_id_map().expect("failed to build forest");
1384 let root_id = remapping.get(root_id).expect("root node should be retained");
1385 let left_id = remapping.get(left_id).expect("left node should be retained");
1386 let right_id = remapping.get(right_id).expect("right node should be retained");
1387 (forest, root_id, left_id, right_id)
1388 }
1389
1390 fn absolute_path(name: &str) -> Arc<AstPath> {
1391 let path = PathBuf::new(name).expect("invalid path");
1392 let path = path.as_path().to_absolute().unwrap().into_owned();
1393 Arc::from(path.into_boxed_path())
1394 }
1395
1396 fn relative_path(name: &str) -> Arc<AstPath> {
1397 let path = PathBuf::relative(name);
1398 Arc::from(path.into_boxed_path())
1399 }
1400
1401 fn build_package_exports(export: &str) -> (Arc<MastForest>, Vec<PackageExport>) {
1402 let (forest, node_id) = build_forest();
1403 let root = forest[node_id].digest();
1404 let path = absolute_path(export);
1405 let export = ProcedureExport::new(Arc::clone(&path), Some(node_id), root, None);
1406
1407 (Arc::new(forest), vec![PackageExport::Procedure(export)])
1408 }
1409
1410 fn build_split_package_exports(
1411 export: &str,
1412 source_node: Option<DebugSourceNodeId>,
1413 ) -> (Arc<MastForest>, Vec<PackageExport>, MastNodeId, MastNodeId, MastNodeId) {
1414 let (forest, root_id, left_id, right_id) = build_split_forest();
1415 let root = forest[root_id].digest();
1416 let path = absolute_path(export);
1417 let export = ProcedureExport::new(Arc::clone(&path), Some(root_id), root, None)
1418 .with_source_node(source_node);
1419
1420 (
1421 Arc::new(forest),
1422 vec![PackageExport::Procedure(export)],
1423 root_id,
1424 left_id,
1425 right_id,
1426 )
1427 }
1428
1429 fn build_same_digest_package_exports(
1430 exports: &[(&str, &str)],
1431 ) -> (Arc<MastForest>, Vec<PackageExport>, Vec<Section>) {
1432 let mut nodes = IndexVec::<MastNodeId, MastNode>::new();
1433 let mut roots = Vec::new();
1434 let mut new_exports = vec![];
1435 let mut source_nodes = Vec::new();
1436 let mut asm_ops = Vec::new();
1437
1438 for (source_idx, (path_str, context_name)) in exports.iter().enumerate() {
1439 let node = BasicBlockNodeBuilder::new(vec![Operation::Add])
1440 .build()
1441 .expect("failed to build basic block");
1442 let num_ops = node.num_operations() as usize;
1443 let digest = node.digest();
1444 let node_id = nodes.push(node.into()).expect("failed to add basic block");
1445 let source_node = DebugSourceNodeId::from(source_idx as u32);
1446 source_nodes.push(DebugSourceNode::new(node_id, Vec::new(), 0, num_ops as u32));
1447 asm_ops.push(DebugSourceAsmOp::new(
1448 source_node,
1449 0,
1450 None,
1451 (*context_name).into(),
1452 "add".into(),
1453 1,
1454 ));
1455 roots.push(node_id);
1456
1457 let path = absolute_path(path_str);
1458 new_exports.push(PackageExport::Procedure(
1459 ProcedureExport::new(path, Some(node_id), digest, None)
1460 .with_source_node(Some(source_node)),
1461 ));
1462 }
1463
1464 let source_graph = DebugSourceGraphSection::from_parts(
1465 source_nodes,
1466 (0..exports.len())
1467 .map(|source_idx| DebugSourceNodeId::from(source_idx as u32))
1468 .collect(),
1469 );
1470 let source_map = DebugSourceMapSection::from_parts(asm_ops, Vec::new());
1471 let sections = vec![
1472 Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes()),
1473 Section::new(SectionId::DEBUG_SOURCE_MAP, source_map.to_bytes()),
1474 ];
1475
1476 let forest = MastForest::from_raw_parts(nodes, roots, AdviceMap::default())
1477 .expect("failed to build forest");
1478 (Arc::new(forest), new_exports, sections)
1479 }
1480
1481 fn build_package(
1482 name: &str,
1483 kind: TargetType,
1484 export: &str,
1485 dependencies: impl IntoIterator<Item = Dependency>,
1486 sections: Vec<Section>,
1487 ) -> Package {
1488 let (mast, exports) = build_package_exports(export);
1489 let mut package = Package::create(
1490 PackageId::from(name),
1491 Version::new(1, 0, 0),
1492 kind,
1493 mast,
1494 exports,
1495 dependencies,
1496 )
1497 .unwrap();
1498 package.sections = sections;
1499 package
1500 }
1501
1502 fn build_kernel_package(name: &str) -> Package {
1503 build_package(name, TargetType::Kernel, &format!("{name}::boot"), [], Vec::new())
1504 }
1505
1506 #[test]
1507 fn package_digest_changes_when_advice_map_changes() {
1508 let package = build_kernel_package("kernel");
1509 let package_digest = package.digest();
1510 let interface_digest = package.interface_digest().unwrap();
1511 let content_digest = package.content_digest();
1512 let mast_commitment = package.mast_forest().commitment();
1513
1514 let advice_map = AdviceMap::from_iter([(
1515 Word::from([1_u32, 2, 3, 4]),
1516 vec![Felt::from_u32(5), Felt::from_u32(6)],
1517 )]);
1518 let with_advice = package.with_advice_map(advice_map);
1519
1520 assert_ne!(package_digest, with_advice.digest());
1521 assert_eq!(interface_digest, with_advice.interface_digest().unwrap());
1522 assert_ne!(content_digest, with_advice.content_digest());
1523 assert_ne!(mast_commitment, with_advice.mast_forest().commitment());
1524 }
1525
1526 fn build_debug_package(name: &str, kind: TargetType, export: &str, context: &str) -> Package {
1527 let (mast, exports, sections) = build_same_digest_package_exports(&[(export, context)]);
1528 let mut package = Package::create(
1529 PackageId::from(name),
1530 Version::new(1, 0, 0),
1531 kind,
1532 mast,
1533 exports,
1534 None,
1535 )
1536 .unwrap();
1537 package.sections = sections;
1538 package
1539 }
1540
1541 fn debug_sections() -> Vec<Section> {
1542 vec![
1543 Section::new(SectionId::DEBUG_SOURCES, vec![1, 2, 3]),
1544 Section::new(SectionId::DEBUG_FUNCTIONS, vec![4, 5, 6]),
1545 Section::new(SectionId::DEBUG_TYPES, vec![7, 8, 9]),
1546 Section::new(SectionId::DEBUG_SOURCE_GRAPH, vec![10, 11, 12]),
1547 Section::new(SectionId::DEBUG_SOURCE_MAP, vec![13, 14, 15]),
1548 Section::new(SectionId::DEBUG_ERROR_MESSAGES, vec![16, 17, 18]),
1549 ]
1550 }
1551
1552 #[test]
1553 fn package_without_debug_sections_has_no_package_debug_info() {
1554 let package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1555
1556 assert!(package.debug_info().unwrap().is_none());
1557 }
1558
1559 #[test]
1560 fn package_debug_info_decodes_source_graph_and_map() {
1561 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1562 let exec_node = package.get_export_node_id("app::entry");
1563 let source_node = DebugSourceNodeId::from(0);
1564 let source_graph = DebugSourceGraphSection::from_parts(
1565 vec![DebugSourceNode::new(exec_node, Vec::new(), 0, 1)],
1566 vec![source_node],
1567 );
1568 let source_map = DebugSourceMapSection::from_parts(
1569 vec![DebugSourceAsmOp::new(
1570 source_node,
1571 0,
1572 None,
1573 "app::entry".into(),
1574 "add".into(),
1575 1,
1576 )],
1577 Vec::new(),
1578 );
1579 package.sections = vec![
1580 Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes()),
1581 Section::new(SectionId::DEBUG_SOURCE_MAP, source_map.to_bytes()),
1582 ];
1583
1584 let debug_info = package
1585 .debug_info()
1586 .expect("debug sections should decode")
1587 .expect("debug sections should be present");
1588
1589 assert_eq!(debug_info.source_node(source_node).unwrap().exec_node, exec_node);
1590 assert_eq!(
1591 debug_info.asm_op_for_operation(source_node, 0).unwrap().context_name,
1592 "app::entry"
1593 );
1594 }
1595
1596 #[test]
1597 fn package_debug_info_rejects_duplicate_debug_sections() {
1598 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1599 package.sections = vec![
1600 Section::new(SectionId::DEBUG_SOURCE_MAP, DebugSourceMapSection::new().to_bytes()),
1601 Section::new(SectionId::DEBUG_SOURCE_MAP, DebugSourceMapSection::new().to_bytes()),
1602 ];
1603
1604 let error = package.debug_info().expect_err("duplicate debug sections should be rejected");
1605
1606 assert!(matches!(
1607 error,
1608 PackageDebugInfoError::DuplicateSection { id } if id == SectionId::DEBUG_SOURCE_MAP
1609 ));
1610 }
1611
1612 #[test]
1613 fn package_debug_info_rejects_malformed_debug_sections() {
1614 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1615 package.sections = vec![Section::new(SectionId::DEBUG_SOURCE_GRAPH, vec![u8::MAX])];
1616
1617 let error = package.debug_info().expect_err("malformed debug sections should be rejected");
1618
1619 assert!(matches!(
1620 error,
1621 PackageDebugInfoError::DecodeSection { id, .. } if id == SectionId::DEBUG_SOURCE_GRAPH
1622 ));
1623 }
1624
1625 #[test]
1626 fn package_debug_info_rejects_invalid_non_source_graph_table_indices() {
1627 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1628
1629 let mut types = DebugTypesSection::new();
1630 types
1631 .types
1632 .push(DebugTypeInfo::Pointer { pointee_type_idx: DebugTypeIdx::from(99) });
1633 package.sections = vec![Section::new(SectionId::DEBUG_TYPES, types.to_bytes())];
1634 assert!(matches!(
1635 package.debug_info(),
1636 Err(PackageDebugInfoError::InvalidReference { .. })
1637 ));
1638
1639 let mut sources = DebugSourcesSection::new();
1640 sources.files.push(DebugFileInfo::new(0));
1641 package.sections = vec![Section::new(SectionId::DEBUG_SOURCES, sources.to_bytes())];
1642 assert!(matches!(
1643 package.debug_info(),
1644 Err(PackageDebugInfoError::InvalidReference { .. })
1645 ));
1646
1647 let mut functions = DebugFunctionsSection::new();
1648 let name_idx = functions.add_string(Arc::from("app::entry"));
1649 functions.add_function(DebugFunctionInfo::new(
1650 name_idx,
1651 0,
1652 LineNumber::new(1).unwrap(),
1653 ColumnNumber::new(1).unwrap(),
1654 ));
1655 package.sections = vec![Section::new(SectionId::DEBUG_FUNCTIONS, functions.to_bytes())];
1656 assert!(matches!(
1657 package.debug_info(),
1658 Err(PackageDebugInfoError::InvalidReference { .. })
1659 ));
1660 }
1661
1662 #[test]
1663 fn package_debug_info_rejects_invalid_inline_call_indices() {
1664 let source_node = DebugSourceNodeId::from(0);
1665 let mut sources = DebugSourcesSection::new();
1666 let path_idx = sources.add_string(Arc::from("app.masm"));
1667 sources.add_file(DebugFileInfo::new(path_idx));
1668
1669 let mut functions = DebugFunctionsSection::new();
1670 let name_idx = functions.add_string(Arc::from("app::entry"));
1671 functions.add_function(DebugFunctionInfo::new(
1672 name_idx,
1673 0,
1674 LineNumber::new(1).unwrap(),
1675 ColumnNumber::new(1).unwrap(),
1676 ));
1677
1678 let source_graph = DebugSourceGraphSection::from_parts(
1679 vec![DebugSourceNode::new(MastNodeId::new_unchecked(0), vec![], 0, 1)],
1680 vec![source_node],
1681 );
1682 let source_map = DebugSourceMapSection::from_parts_with_inline_calls(
1683 Vec::new(),
1684 Vec::new(),
1685 vec![DebugSourceInlineCall::new(
1686 source_node,
1687 0,
1688 1,
1689 0,
1690 LineNumber::new(1).unwrap(),
1691 ColumnNumber::new(1).unwrap(),
1692 )],
1693 );
1694 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1695 package.sections = vec![
1696 Section::new(SectionId::DEBUG_SOURCES, sources.to_bytes()),
1697 Section::new(SectionId::DEBUG_FUNCTIONS, functions.to_bytes()),
1698 Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes()),
1699 Section::new(SectionId::DEBUG_SOURCE_MAP, source_map.to_bytes()),
1700 ];
1701
1702 let err = package.debug_info().expect_err("bad inline call index should be rejected");
1703 assert!(matches!(err, PackageDebugInfoError::InvalidReference { .. }));
1704 }
1705
1706 #[test]
1707 fn package_debug_info_rejects_source_graph_child_exec_mismatch() {
1708 let source_root = DebugSourceNodeId::from(0);
1709 let source_left = DebugSourceNodeId::from(1);
1710 let source_right = DebugSourceNodeId::from(2);
1711 let (mast, exports, root_id, left_id, right_id) =
1712 build_split_package_exports("app::entry", Some(source_root));
1713 let mut package = Package::create(
1714 PackageId::from("app"),
1715 Version::new(1, 0, 0),
1716 TargetType::Library,
1717 mast,
1718 exports,
1719 None,
1720 )
1721 .unwrap();
1722 let source_graph = DebugSourceGraphSection::from_parts(
1723 vec![
1724 DebugSourceNode::new(root_id, vec![source_right, source_left], 0, 1),
1725 DebugSourceNode::new(left_id, Vec::new(), 0, 1),
1726 DebugSourceNode::new(right_id, Vec::new(), 0, 1),
1727 ],
1728 vec![source_root],
1729 );
1730 package.sections =
1731 vec![Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes())];
1732
1733 let error = package.debug_info().expect_err("mismatched source child should be rejected");
1734
1735 assert!(matches!(error, PackageDebugInfoError::InvalidReference { .. }));
1736 }
1737
1738 #[test]
1739 fn package_debug_info_rejects_invalid_source_node_operation_ranges() {
1740 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1741 let exec_node = package.get_export_node_id("app::entry");
1742 let source_node = DebugSourceNodeId::from(0);
1743
1744 for (op_start, op_end) in [(1, 0), (0, 2)] {
1745 let source_graph = DebugSourceGraphSection::from_parts(
1746 vec![DebugSourceNode::new(exec_node, Vec::new(), op_start, op_end)],
1747 vec![source_node],
1748 );
1749 package.sections =
1750 vec![Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes())];
1751
1752 let error = package
1753 .debug_info()
1754 .expect_err("invalid source node operation range should be rejected");
1755
1756 assert!(matches!(error, PackageDebugInfoError::InvalidReference { .. }));
1757 }
1758 }
1759
1760 #[test]
1761 fn package_debug_info_rejects_source_map_missing_source_node() {
1762 let mut package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
1763 let exec_node = package.get_export_node_id("app::entry");
1764 let source_node = DebugSourceNodeId::from(0);
1765 let missing_source_node = DebugSourceNodeId::from(1);
1766 let source_graph = DebugSourceGraphSection::from_parts(
1767 vec![DebugSourceNode::new(exec_node, Vec::new(), 0, 1)],
1768 vec![source_node],
1769 );
1770 let source_map = DebugSourceMapSection::from_parts(
1771 vec![DebugSourceAsmOp::new(
1772 missing_source_node,
1773 0,
1774 None,
1775 "app::entry".into(),
1776 "add".into(),
1777 1,
1778 )],
1779 Vec::new(),
1780 );
1781 package.sections = vec![
1782 Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes()),
1783 Section::new(SectionId::DEBUG_SOURCE_MAP, source_map.to_bytes()),
1784 ];
1785
1786 let error = package
1787 .debug_info()
1788 .expect_err("source map row with missing source node should be rejected");
1789
1790 assert!(matches!(error, PackageDebugInfoError::InvalidReference { .. }));
1791 }
1792
1793 #[test]
1794 fn package_debug_info_rejects_export_source_node_exec_mismatch() {
1795 let source_root = DebugSourceNodeId::from(0);
1796 let source_left = DebugSourceNodeId::from(1);
1797 let source_right = DebugSourceNodeId::from(2);
1798 let (mast, exports, root_id, left_id, right_id) =
1799 build_split_package_exports("app::entry", Some(source_left));
1800 let mut package = Package::create(
1801 PackageId::from("app"),
1802 Version::new(1, 0, 0),
1803 TargetType::Library,
1804 mast,
1805 exports,
1806 None,
1807 )
1808 .unwrap();
1809 let source_graph = DebugSourceGraphSection::from_parts(
1810 vec![
1811 DebugSourceNode::new(root_id, vec![source_left, source_right], 0, 1),
1812 DebugSourceNode::new(left_id, Vec::new(), 0, 1),
1813 DebugSourceNode::new(right_id, Vec::new(), 0, 1),
1814 ],
1815 vec![source_root],
1816 );
1817 package.sections =
1818 vec![Section::new(SectionId::DEBUG_SOURCE_GRAPH, source_graph.to_bytes())];
1819
1820 let error = package
1821 .debug_info()
1822 .expect_err("export source node mapped to child exec node should be rejected");
1823
1824 assert!(matches!(error, PackageDebugInfoError::InvalidReference { .. }));
1825 }
1826
1827 #[test]
1828 fn to_kernel_rejects_empty_kernel_exports() {
1829 let mut package = build_package("kernel", TargetType::Kernel, "$kernel::boot", [], vec![]);
1830 package.manifest = PackageManifest {
1831 exports: Default::default(),
1832 modules: Default::default(),
1833 dependencies: Default::default(),
1834 entrypoint: None,
1835 };
1836
1837 let error = package
1838 .to_kernel()
1839 .expect_err("kernel packages without exported procedures should be rejected");
1840
1841 assert!(
1842 error
1843 .to_string()
1844 .contains("invalid kernel package: does not export any kernel procedures")
1845 );
1846 }
1847
1848 fn kernel_dependency(package: &Package) -> Dependency {
1849 Dependency {
1850 name: package.name.clone(),
1851 kind: TargetType::Kernel,
1852 version: package.version.clone(),
1853 digest: package.digest(),
1854 }
1855 }
1856
1857 #[test]
1858 fn embedded_kernel_package_rejects_duplicate_kernel_sections() {
1859 let kernel = build_kernel_package("kernel");
1860 let kernel_bytes = kernel.to_bytes();
1861 let package = build_package(
1862 "app",
1863 TargetType::Library,
1864 "app::entry",
1865 vec![kernel_dependency(&kernel)],
1866 vec![
1867 Section::new(SectionId::KERNEL, kernel_bytes.clone()),
1868 Section::new(SectionId::KERNEL, kernel_bytes),
1869 ],
1870 );
1871
1872 let error = package
1873 .try_embedded_kernel_package()
1874 .expect_err("duplicate kernel sections should be rejected");
1875
1876 assert!(error.to_string().contains("multiple 'kernel' sections"));
1877 }
1878
1879 #[test]
1880 fn embedded_kernel_package_rejects_multiple_kernel_runtime_dependencies() {
1881 let kernel_a = build_kernel_package("kernel-a");
1882 let kernel_b = build_kernel_package("kernel-b");
1883 let package = build_package(
1884 "app",
1885 TargetType::Library,
1886 "app::entry",
1887 vec![kernel_dependency(&kernel_a), kernel_dependency(&kernel_b)],
1888 vec![Section::new(SectionId::KERNEL, kernel_a.to_bytes())],
1889 );
1890
1891 let error = package
1892 .try_embedded_kernel_package()
1893 .expect_err("multiple kernel runtime dependencies should be rejected");
1894
1895 assert!(error.to_string().contains("declares multiple kernel runtime dependencies"));
1896 }
1897
1898 #[test]
1899 fn untrusted_embedded_kernel_decode_discards_nested_debug_info() {
1900 let kernel =
1901 build_debug_package("kernel", TargetType::Kernel, "kernel::boot", "kernel_ctx");
1902 assert!(kernel.debug_info().unwrap().is_some());
1903
1904 let package = build_package(
1905 "app",
1906 TargetType::Executable,
1907 "app::entry",
1908 vec![kernel_dependency(&kernel)],
1909 vec![Section::new(SectionId::KERNEL, kernel.to_bytes())],
1910 );
1911
1912 let round_tripped = Package::read_from_bytes(&package.to_bytes())
1913 .expect("untrusted package read should succeed");
1914 let raw_kernel_bytes = round_tripped
1915 .sections
1916 .iter()
1917 .find(|section| section.id == SectionId::KERNEL)
1918 .expect("kernel section should remain available as opaque bytes")
1919 .data
1920 .as_ref();
1921 let trusted_kernel = Package::read_from_bytes_trusted(raw_kernel_bytes)
1922 .expect("trusted direct kernel read should succeed");
1923 assert!(
1924 trusted_kernel.debug_info().unwrap().is_some(),
1925 "opaque kernel bytes may still contain trusted-cache debug metadata"
1926 );
1927
1928 let untrusted_kernel = round_tripped
1929 .try_embedded_kernel_package()
1930 .expect("embedded kernel should decode")
1931 .expect("kernel should be present");
1932 assert!(
1933 !untrusted_kernel.sections.iter().any(|section| section.id.is_debug()),
1934 "untrusted embedded-kernel decode should discard nested debug sections"
1935 );
1936 assert!(untrusted_kernel.debug_info().unwrap().is_none());
1937 }
1938
1939 #[test]
1940 fn strip_debug_info_removes_package_and_embedded_kernel_debug() {
1941 let mut kernel =
1942 build_debug_package("kernel", TargetType::Kernel, "kernel::boot", "kernel_ctx");
1943 kernel.sections = debug_sections();
1944 kernel
1945 .sections
1946 .push(Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, vec![42, 43, 44]));
1947 assert!(kernel.sections.iter().any(|section| section.id.is_debug()));
1948
1949 let mut package =
1950 build_debug_package("app", TargetType::Executable, "app::entry", "app_ctx");
1951 let digest = package.digest();
1952 package.sections = debug_sections();
1953 package
1954 .sections
1955 .push(Section::new(SectionId::ACCOUNT_COMPONENT_METADATA, vec![1, 3, 5]));
1956 package.sections.push(Section::new(SectionId::KERNEL, kernel.to_bytes()));
1957 let content_digest = package.content_digest();
1958 assert!(package.sections.iter().any(|section| section.id.is_debug()));
1959
1960 package.strip_debug_info().expect("strip should succeed");
1961
1962 assert_eq!(package.digest(), digest);
1963 assert_eq!(package.content_digest(), content_digest);
1964 assert!(!package.sections.iter().any(|section| section.id.is_debug()));
1965 assert!(
1966 package
1967 .sections
1968 .iter()
1969 .any(|section| section.id == SectionId::ACCOUNT_COMPONENT_METADATA)
1970 );
1971
1972 let stripped_kernel = package
1973 .embedded_kernel_package()
1974 .unwrap()
1975 .expect("kernel should remain embedded");
1976 assert!(!stripped_kernel.sections.iter().any(|section| section.id.is_debug()));
1977 assert!(
1978 stripped_kernel
1979 .sections
1980 .iter()
1981 .any(|section| section.id == SectionId::ACCOUNT_COMPONENT_METADATA)
1982 );
1983
1984 let raw_kernel_bytes = package
1985 .sections
1986 .iter()
1987 .find(|section| section.id == SectionId::KERNEL)
1988 .expect("kernel section should remain embedded")
1989 .data
1990 .as_ref();
1991 let trusted_stripped_kernel = Package::read_from_bytes_trusted(raw_kernel_bytes)
1992 .expect("trusted stripped kernel read should succeed");
1993 assert!(
1994 !trusted_stripped_kernel.sections.iter().any(|section| section.id.is_debug()),
1995 "stripping should remove nested debug sections from raw kernel bytes"
1996 );
1997 }
1998
1999 #[test]
2000 fn malformed_procedure_lookup_paths_are_not_exported() {
2001 let package = build_package("app", TargetType::Library, "app::entry", [], Vec::new());
2002 let invalid_path = alloc::format!("::{}", "a".repeat(AstPath::MAX_COMPONENT_LENGTH + 1));
2003 let invalid_path = AstPath::new(&invalid_path);
2004
2005 assert_eq!(package.get_procedure_root_by_path(invalid_path), None);
2006 assert_eq!(package.get_procedure_node_by_path(invalid_path), None);
2007 assert!(!package.is_reexport(invalid_path));
2008 }
2009
2010 #[test]
2011 fn procedure_lookup_accepts_relative_and_absolute_export_paths() {
2012 let (forest, node_id) = build_forest();
2013 let digest = forest[node_id].digest();
2014 let path = relative_path("app::entry");
2015 let export =
2016 PackageExport::Procedure(ProcedureExport::new(path, Some(node_id), digest, None));
2017 let package = Package::create(
2018 PackageId::from("app"),
2019 Version::new(1, 0, 0),
2020 TargetType::Library,
2021 Arc::new(forest),
2022 vec![export],
2023 None,
2024 )
2025 .expect("package should be valid");
2026
2027 assert_eq!(package.get_procedure_root_by_path("app::entry"), Some(digest));
2028 assert_eq!(package.get_procedure_root_by_path("::app::entry"), Some(digest));
2029 assert_eq!(package.get_procedure_node_by_path("app::entry"), Some(node_id));
2030 assert_eq!(package.get_procedure_node_by_path("::app::entry"), Some(node_id));
2031 assert_eq!(package.get_export_node_id("::app::entry"), node_id);
2032 assert!(!package.is_reexport("::app::entry"));
2033 }
2034
2035 #[test]
2036 fn make_executable_preserves_selected_same_digest_root_metadata() {
2037 let (mast, exports, sections) = build_same_digest_package_exports(&[
2038 ("app::alias_a", "alias_a"),
2039 ("app::alias_b", "alias_b"),
2040 ]);
2041 let mut package = Package::create(
2042 PackageId::from("app"),
2043 Version::new(1, 0, 0),
2044 TargetType::Library,
2045 mast,
2046 exports,
2047 None,
2048 )
2049 .expect("package should be valid");
2050 package.sections = sections;
2051
2052 let entrypoint = QualifiedProcedureName::from_str("app::alias_b").unwrap();
2053 let executable = package.make_executable(&entrypoint).unwrap();
2054
2055 let main_path = Path::exec_path().join(ProcedureName::MAIN_PROC_NAME);
2056 let entrypoint_node = executable.get_procedure_node_by_path(&main_path).unwrap();
2057 let main_export = executable
2058 .manifest
2059 .get_export(&main_path)
2060 .and_then(PackageExport::as_procedure)
2061 .expect("main export should exist");
2062 let source_node = main_export.source_node.expect("main export should retain source node");
2063 let debug_info = executable
2064 .debug_info()
2065 .expect("debug sections should decode")
2066 .expect("debug sections should be present");
2067
2068 assert_eq!(debug_info.source_node(source_node).unwrap().exec_node, entrypoint_node);
2069 assert_eq!(
2070 debug_info.first_asm_op_for_source_node(source_node).unwrap().context_name,
2071 "alias_b"
2072 );
2073
2074 let program = executable.try_into_program().unwrap();
2075 assert_eq!(program.entrypoint(), entrypoint_node);
2076 }
2077
2078 #[test]
2079 fn make_executable_preserves_debug_section_trust_state() {
2080 let (mast, exports, sections) = build_same_digest_package_exports(&[
2081 ("app::alias_a", "alias_a"),
2082 ("app::alias_b", "alias_b"),
2083 ]);
2084 let mut package = Package::create(
2085 PackageId::from("app"),
2086 Version::new(1, 0, 0),
2087 TargetType::Library,
2088 mast,
2089 exports,
2090 None,
2091 )
2092 .expect("package should be valid");
2093 package.sections = sections;
2094 package.debug_sections_trusted = false;
2095
2096 let executable = package
2097 .make_executable(&QualifiedProcedureName::from_str("app::alias_b").unwrap())
2098 .unwrap();
2099
2100 assert!(!executable.debug_sections_trusted);
2101 assert_matches!(executable.debug_info(), Err(PackageDebugInfoError::UntrustedSections));
2102 }
2103
2104 #[test]
2105 fn make_executable_accepts_relative_entrypoint_export_path() {
2106 let (forest, node_id) = build_forest();
2107 let digest = forest[node_id].digest();
2108 let path = relative_path("app::entry");
2109 let export =
2110 PackageExport::Procedure(ProcedureExport::new(path, Some(node_id), digest, None));
2111 let package = Package::create(
2112 PackageId::from("app"),
2113 Version::new(1, 0, 0),
2114 TargetType::Library,
2115 Arc::new(forest),
2116 [export],
2117 None,
2118 )
2119 .expect("package should be valid");
2120
2121 let entrypoint = QualifiedProcedureName::from_str("app::entry").unwrap();
2122 let executable = package.make_executable(&entrypoint).unwrap();
2123
2124 let main_path = Path::exec_path().join(ProcedureName::MAIN_PROC_NAME);
2125 assert_eq!(executable.get_procedure_root_by_path(&main_path), Some(digest));
2126 assert_eq!(executable.get_procedure_node_by_path(&main_path), Some(node_id));
2127 }
2128
2129 #[test]
2130 fn merge_source_debug_keeps_concrete_metadata_distinct_from_external_placeholder() {
2131 fn debug_info_for_root(root: MastNodeId, context: &str) -> PackageDebugInfo {
2132 let source_node = DebugSourceNodeId::from(0);
2133 PackageDebugInfo {
2134 source_graph: Some(DebugSourceGraphSection::from_parts(
2135 vec![DebugSourceNode::new(root, vec![], 0, 1)],
2136 vec![source_node],
2137 )),
2138 source_map: Some(DebugSourceMapSection::from_parts(
2139 vec![DebugSourceAsmOp::new(
2140 source_node,
2141 0,
2142 None,
2143 context.into(),
2144 "add".into(),
2145 1,
2146 )],
2147 Vec::new(),
2148 )),
2149 ..PackageDebugInfo::default()
2150 }
2151 }
2152
2153 let mut concrete_builder = DenseMastForestBuilder::new();
2154 let concrete_root = concrete_builder
2155 .push_node(BasicBlockNodeBuilder::new(vec![Operation::Add]))
2156 .unwrap();
2157 concrete_builder.mark_root(concrete_root);
2158 let (concrete_forest, concrete_remapping) = concrete_builder.finish_with_id_map().unwrap();
2159 let concrete_root = concrete_remapping.get(concrete_root).unwrap();
2160 let concrete_digest = concrete_forest[concrete_root].digest();
2161
2162 let mut placeholder_builder = DenseMastForestBuilder::new();
2163 let placeholder_root = placeholder_builder
2164 .push_node(ExternalNodeBuilder::new(concrete_digest))
2165 .unwrap();
2166 placeholder_builder.mark_root(placeholder_root);
2167 let (placeholder_forest, placeholder_remapping) =
2168 placeholder_builder.finish_with_id_map().unwrap();
2169 let placeholder_root = placeholder_remapping.get(placeholder_root).unwrap();
2170
2171 let placeholder_debug = debug_info_for_root(placeholder_root, "placeholder");
2172 let concrete_debug = debug_info_for_root(concrete_root, "concrete");
2173
2174 let (_merged_forest, root_map) =
2175 MastForest::merge([&placeholder_forest, &concrete_forest]).unwrap();
2176 let merged_placeholder = root_map.map_root(0, &placeholder_root).unwrap();
2177 let merged_concrete = root_map.map_root(1, &concrete_root).unwrap();
2178 assert_eq!(merged_placeholder, merged_concrete);
2179
2180 let merged_debug = PackageDebugInfo::merge_source_debug(
2181 [(0, &placeholder_debug), (1, &concrete_debug)],
2182 &root_map,
2183 )
2184 .unwrap();
2185 let source_graph = merged_debug.source_graph.as_ref().unwrap();
2186 assert_eq!(source_graph.nodes().len(), 2);
2187 assert!(source_graph.nodes().iter().all(|node| node.exec_node == merged_concrete));
2188
2189 let placeholder_source = source_graph.roots()[0];
2190 let concrete_source = source_graph.roots()[1];
2191 assert_ne!(placeholder_source, concrete_source);
2192 assert_eq!(
2193 merged_debug
2194 .first_asm_op_for_source_node(placeholder_source)
2195 .unwrap()
2196 .context_name,
2197 "placeholder",
2198 );
2199 assert_eq!(
2200 merged_debug.first_asm_op_for_source_node(concrete_source).unwrap().context_name,
2201 "concrete",
2202 );
2203 }
2204
2205 #[test]
2206 fn make_executable_same_digest_selection_is_export_order_independent() {
2207 fn selected_context_for_alias_b(exports: &[(&str, &str)]) -> String {
2208 let (mast, exports, sections) = build_same_digest_package_exports(exports);
2209 let mut package = Package::create(
2210 PackageId::from("app"),
2211 Version::new(1, 0, 0),
2212 TargetType::Library,
2213 mast,
2214 exports,
2215 None,
2216 )
2217 .expect("package should be valid");
2218 package.sections = sections;
2219
2220 let executable = package
2221 .make_executable(&QualifiedProcedureName::from_str("app::alias_b").unwrap())
2222 .unwrap();
2223 let main_path = Path::exec_path().join(ProcedureName::MAIN_PROC_NAME);
2224 let main_export = executable
2225 .manifest
2226 .get_export(&main_path)
2227 .and_then(PackageExport::as_procedure)
2228 .expect("main export should exist");
2229 let source_node =
2230 main_export.source_node.expect("main export should retain source node");
2231 let debug_info = executable
2232 .debug_info()
2233 .expect("debug sections should decode")
2234 .expect("debug sections should be present");
2235
2236 debug_info
2237 .first_asm_op_for_source_node(source_node)
2238 .unwrap()
2239 .context_name
2240 .clone()
2241 }
2242
2243 assert_eq!(
2244 selected_context_for_alias_b(&[
2245 ("app::alias_a", "alias_a"),
2246 ("app::alias_b", "alias_b")
2247 ]),
2248 "alias_b",
2249 );
2250 assert_eq!(
2251 selected_context_for_alias_b(&[
2252 ("app::alias_b", "alias_b"),
2253 ("app::alias_a", "alias_a")
2254 ]),
2255 "alias_b",
2256 );
2257 }
2258}