1#[cfg(feature = "arbitrary")]
9mod arbitrary;
10mod builder;
11mod serialization;
12mod types;
13
14use alloc::{sync::Arc, vec::Vec};
15
16pub use builder::*;
17use miden_core::mast::{MastForestRootMap, MastNodeId};
18#[cfg(all(feature = "arbitrary", test))]
19use miden_core::serde::{Deserializable, Serializable};
20use miden_debug_types::{Location, Uri};
21use miden_utils_indexing::{Idx, IndexVec};
22pub use types::*;
23
24type FxHashMap<K, V> = hashbrown::HashMap<K, V, rustc_hash::FxBuildHasher>;
25type FxHashSet<K> = hashbrown::HashSet<K, rustc_hash::FxBuildHasher>;
26
27pub const DEBUG_INFO_VERSION: u8 = 2;
28
29#[cfg_attr(
34 all(feature = "arbitrary", test),
35 miden_test_serde_macros::serde_test(binary_serde(true), serde_test(false))
36)]
37pub type PackageDebugInfo = DebugInfo<MastNodeId, DebugSourceNodeId>;
38
39#[derive(Eq, PartialEq)]
45pub struct DebugInfo<Exec: Idx, Src: Idx> {
46 version: u8,
48 strings: IndexVec<DebugStringIdx, Arc<str>>,
50 files: IndexVec<DebugFileIdx, DebugFileInfo>,
56 locations: IndexVec<DebugLocIdx, DebugLoc>,
60 types: IndexVec<DebugTypeIdx, DebugTypeInfo>,
62 functions: IndexVec<DebugFunctionIdx, FunctionInfo<Src>>,
71 nodes: IndexVec<Src, SourceNode<Exec, Src>>,
77 roots: Vec<Src>,
81 error_messages: Vec<DebugErrorMessage>,
83}
84
85#[derive(Clone, Debug, Default)]
90pub struct DebugInfoTableRemapping {
91 strings: IndexVec<DebugStringIdx, DebugStringIdx>,
92 files: IndexVec<DebugFileIdx, DebugFileIdx>,
93 locations: IndexVec<DebugLocIdx, DebugLocIdx>,
94 types: IndexVec<DebugTypeIdx, DebugTypeIdx>,
95 functions: IndexVec<DebugFunctionIdx, DebugFunctionIdx>,
98}
99
100impl DebugInfoTableRemapping {
101 pub fn string(&self, index: DebugStringIdx) -> Option<DebugStringIdx> {
103 self.strings.get(index).copied()
104 }
105
106 pub fn file(&self, index: DebugFileIdx) -> Option<DebugFileIdx> {
108 self.files.get(index).copied()
109 }
110
111 pub fn location(&self, index: DebugLocIdx) -> Option<DebugLocIdx> {
113 self.locations.get(index).copied()
114 }
115
116 pub fn ty(&self, index: DebugTypeIdx) -> Option<DebugTypeIdx> {
118 self.types.get(index).copied()
119 }
120
121 pub fn function(&self, index: DebugFunctionIdx) -> Option<DebugFunctionIdx> {
123 self.functions.get(index).copied()
124 }
125}
126
127impl<Exec: Idx, Src: Idx> Default for DebugInfo<Exec, Src> {
131 fn default() -> Self {
132 Self {
133 version: DEBUG_INFO_VERSION,
134 strings: Default::default(),
135 files: Default::default(),
136 locations: Default::default(),
137 types: Default::default(),
138 functions: Default::default(),
139 nodes: Default::default(),
140 roots: Default::default(),
141 error_messages: Default::default(),
142 }
143 }
144}
145
146impl<Exec, Src> Clone for DebugInfo<Exec, Src>
147where
148 Exec: Idx + Clone,
149 Src: Idx + Clone,
150{
151 fn clone(&self) -> Self {
152 Self {
153 version: self.version,
154 strings: self.strings.clone(),
155 files: self.files.clone(),
156 locations: self.locations.clone(),
157 types: self.types.clone(),
158 functions: self.functions.clone(),
159 nodes: self.nodes.clone(),
160 roots: self.roots.clone(),
161 error_messages: self.error_messages.clone(),
162 }
163 }
164}
165
166impl<Exec, Src> core::fmt::Debug for DebugInfo<Exec, Src>
167where
168 Exec: Idx + core::fmt::Debug,
169 Src: Idx + core::fmt::Debug,
170{
171 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172 f.debug_struct("DebugInfo")
173 .field("version", &self.version)
174 .field("strings", &self.strings)
175 .field("files", &self.files)
176 .field("locations", &self.locations)
177 .field("types", &self.types)
178 .field("functions", &self.functions)
179 .field("nodes", &self.nodes)
180 .field("roots", &self.roots)
181 .field("error_messages", &self.error_messages)
182 .finish()
183 }
184}
185
186impl<Exec: Idx, Src: Idx> core::ops::Index<DebugStringIdx> for DebugInfo<Exec, Src> {
190 type Output = Arc<str>;
191
192 fn index(&self, index: DebugStringIdx) -> &Self::Output {
193 &self.strings[index]
194 }
195}
196
197impl<Exec: Idx, Src: Idx> core::ops::Index<DebugFileIdx> for DebugInfo<Exec, Src> {
198 type Output = DebugFileInfo;
199
200 fn index(&self, index: DebugFileIdx) -> &Self::Output {
201 &self.files[index]
202 }
203}
204
205impl<Exec: Idx, Src: Idx> core::ops::Index<DebugFunctionIdx> for DebugInfo<Exec, Src> {
206 type Output = FunctionInfo<Src>;
207
208 fn index(&self, index: DebugFunctionIdx) -> &Self::Output {
209 &self.functions[index]
210 }
211}
212
213impl<Exec: Idx, Src: Idx> core::ops::Index<DebugTypeIdx> for DebugInfo<Exec, Src> {
214 type Output = DebugTypeInfo;
215
216 fn index(&self, index: DebugTypeIdx) -> &Self::Output {
217 &self.types[index]
218 }
219}
220
221impl<Exec: Idx, Src: Idx> core::ops::Index<DebugLocIdx> for DebugInfo<Exec, Src> {
222 type Output = DebugLoc;
223
224 fn index(&self, index: DebugLocIdx) -> &Self::Output {
225 &self.locations[index]
226 }
227}
228
229pub trait SourceNodeIdMarker: Idx + core::hash::Hash {}
233
234impl<Exec: Idx, Src: SourceNodeIdMarker> core::ops::Index<Src> for DebugInfo<Exec, Src> {
235 type Output = SourceNode<Exec, Src>;
236
237 fn index(&self, index: Src) -> &Self::Output {
238 &self.nodes[index]
239 }
240}
241
242impl<Exec: Idx, Src: Idx> DebugInfo<Exec, Src> {
246 pub fn version(&self) -> u8 {
248 self.version
249 }
250
251 pub fn strings(&self) -> &IndexVec<DebugStringIdx, Arc<str>> {
253 &self.strings
254 }
255
256 pub fn get_string(&self, idx: DebugStringIdx) -> Option<Arc<str>> {
258 self.strings.get(idx).cloned()
259 }
260
261 pub fn files(&self) -> &IndexVec<DebugFileIdx, DebugFileInfo> {
263 &self.files
264 }
265
266 pub fn get_file(&self, idx: DebugFileIdx) -> Option<&DebugFileInfo> {
268 self.files.get(idx)
269 }
270
271 pub fn get_file_index_by_uri(&self, uri: &Uri) -> Option<DebugFileIdx> {
274 self.files
275 .iter()
276 .position(|file| {
277 self.strings
278 .get(file.path_idx)
279 .map(|path| path.as_ref() == uri.as_str())
280 .unwrap_or(false)
281 })
282 .map(|pos| DebugFileIdx::from(pos as u32))
283 }
284
285 pub fn trim_file_paths(&mut self, mut trimmer: impl FnMut(&str) -> Option<Arc<str>>) {
291 use hashbrown::hash_map::Entry;
292
293 let mut string_indices = FxHashMap::<Arc<str>, DebugStringIdx>::default();
294 for (index, string) in self.strings.iter().enumerate() {
295 string_indices
296 .entry(string.clone())
297 .or_insert_with(|| DebugStringIdx::from(index as u32));
298 }
299
300 let mut remapped_paths = FxHashMap::<DebugStringIdx, DebugStringIdx>::default();
305 for file in self.files.iter_mut() {
306 let old_path_idx = file.path_idx;
307 let new_path_idx = if let Some(new_path_idx) = remapped_paths.get(&old_path_idx) {
308 *new_path_idx
309 } else {
310 let path = self.strings[old_path_idx].clone();
311 let new_path_idx = match trimmer(path.as_ref()) {
312 None => old_path_idx,
313 Some(new_path) => match string_indices.entry(new_path.clone()) {
314 Entry::Occupied(entry) => *entry.get(),
315 Entry::Vacant(entry) => {
316 let index =
317 self.strings.push(new_path).expect("too many debug info strings");
318 entry.insert(index);
319 index
320 },
321 },
322 };
323 remapped_paths.insert(old_path_idx, new_path_idx);
324 new_path_idx
325 };
326 file.path_idx = new_path_idx;
327 }
328 }
329
330 pub fn types(&self) -> &IndexVec<DebugTypeIdx, DebugTypeInfo> {
332 &self.types
333 }
334
335 pub fn get_type(&self, idx: DebugTypeIdx) -> Option<&DebugTypeInfo> {
337 self.types.get(idx)
338 }
339
340 pub fn locations(&self) -> &IndexVec<DebugLocIdx, DebugLoc> {
342 &self.locations
343 }
344
345 pub fn get_location(&self, idx: DebugLocIdx) -> Option<Location> {
347 let DebugLoc { file_idx, start, end } = self.locations.get(idx)?;
348 let file = &self.files[*file_idx];
349 let uri = self.strings[file.path_idx].clone();
350 Some(Location {
351 uri: Uri::from(uri),
352 start: *start,
353 end: *end,
354 })
355 }
356
357 pub fn error_messages(&self) -> &[DebugErrorMessage] {
359 &self.error_messages
360 }
361
362 pub fn error_message(&self, err_code: u64) -> Option<Arc<str>> {
364 self.error_messages
365 .iter()
366 .find(|row| row.err_code == err_code)
367 .map(|row| self.strings[row.message].clone())
368 }
369
370 pub fn nodes(&self) -> &IndexVec<Src, SourceNode<Exec, Src>> {
372 &self.nodes
373 }
374
375 pub fn roots(&self) -> &[Src] {
377 &self.roots
378 }
379
380 pub fn source_node(&self, source_node: Src) -> Option<&SourceNode<Exec, Src>> {
382 self.nodes.get(source_node)
383 }
384
385 pub fn functions(&self) -> &[FunctionInfo<Src>] {
387 self.functions.as_slice()
388 }
389
390 pub fn get_function(&self, idx: DebugFunctionIdx) -> Option<&FunctionInfo<Src>> {
392 self.functions.get(idx)
393 }
394
395 pub fn source_roots_for_exec_node(
397 &self,
398 exec_node: Exec,
399 ) -> impl Iterator<Item = (Src, &SourceNode<Exec, Src>)> {
400 self.roots.iter().copied().filter_map(move |source_node_id| {
401 let source_node = &self.nodes[source_node_id];
402 if source_node.exec_node == exec_node {
403 Some((source_node_id, source_node))
404 } else {
405 None
406 }
407 })
408 }
409
410 pub fn unique_source_root_for_exec_node(
412 &self,
413 exec_node: Exec,
414 ) -> Result<Option<Src>, SourceGraphLookupError<Exec, Src>> {
415 let mut roots = self
416 .source_roots_for_exec_node(exec_node)
417 .map(|(source_node_id, _)| source_node_id);
418 let first = roots.next();
419 if roots.next().is_some() {
420 return Err(SourceGraphLookupError::AmbiguousRoot { exec_node });
421 }
422 Ok(first)
423 }
424
425 pub fn child_source_node(
427 &self,
428 parent: Src,
429 child_index: usize,
430 ) -> Result<Option<(Src, &SourceNode<Exec, Src>)>, SourceGraphLookupError<Exec, Src>> {
431 let parent_node = self
432 .source_node(parent)
433 .ok_or(SourceGraphLookupError::MissingSourceNode { source_node: parent })?;
434 let Some(child) = parent_node.children.get(child_index).copied() else {
435 return Ok(None);
436 };
437 let child_node = self
438 .source_node(child)
439 .ok_or(SourceGraphLookupError::MissingSourceNode { source_node: child })?;
440
441 Ok(Some((child, child_node)))
442 }
443
444 pub fn asm_ops_for_source_node(
446 &self,
447 source_node: Src,
448 ) -> impl Iterator<Item = &DebugSourceAsmOp> {
449 self.source_node(source_node).into_iter().flat_map(|node| node.asm_ops.iter())
450 }
451
452 pub fn first_asm_op_for_source_node(&self, source_node: Src) -> Option<&DebugSourceAsmOp> {
454 self.asm_ops_for_source_node(source_node).min_by_key(|row| row.op_idx)
455 }
456
457 pub fn asm_op_for_operation(&self, source_node: Src, op_idx: u32) -> Option<&DebugSourceAsmOp> {
459 self.asm_ops_for_source_node(source_node)
460 .filter(|row| row.op_idx <= op_idx)
461 .max_by_key(|row| row.op_idx)
462 }
463
464 pub fn debug_vars_for_source_node(
466 &self,
467 source_node: Src,
468 ) -> impl Iterator<Item = &DebugSourceVar> {
469 self.source_node(source_node)
470 .into_iter()
471 .flat_map(|node| node.debug_vars.iter())
472 }
473
474 pub fn debug_vars_for_operation(
476 &self,
477 source_node: Src,
478 op_idx: u32,
479 ) -> impl Iterator<Item = &DebugSourceVar> {
480 self.debug_vars_for_source_node(source_node)
481 .filter(move |row| row.op_idx == op_idx)
482 }
483
484 pub fn inline_calls_for_source_node(
486 &self,
487 source_node: Src,
488 ) -> impl Iterator<Item = &DebugSourceInlineCall> {
489 self.source_node(source_node)
490 .into_iter()
491 .flat_map(|node| node.inline_calls.iter())
492 }
493
494 pub fn inline_calls_for_operation(
496 &self,
497 source_node: Src,
498 op_idx: u32,
499 ) -> impl Iterator<Item = &DebugSourceInlineCall> {
500 self.inline_calls_for_source_node(source_node)
501 .filter(move |row| row.op_idx == op_idx)
502 }
503}
504
505impl<Exec: Idx, Src: Idx> DebugInfo<Exec, Src> {
506 pub fn merge_tables_into<TargetExec: Idx, TargetSrc: Idx>(
514 &self,
515 target: &mut DebugInfoBuilder<TargetExec, TargetSrc>,
516 ) -> Result<DebugInfoTableRemapping, DebugInfoTableRemapError> {
517 self.validate_shared_table_references()?;
520
521 let mut remapping = DebugInfoTableRemapping::default();
522
523 for (index, string) in self.strings.iter().enumerate() {
524 let source = DebugStringIdx::from(
525 u32::try_from(index).expect("invalid source string table index"),
526 );
527 let inserted = remapping
528 .strings
529 .push(target.add_string(string.clone()))
530 .expect("too many remapped strings");
531 debug_assert_eq!(inserted, source);
532 }
533
534 let type_offset = target.debug_info().types.len();
537 for index in 0..self.types.len() {
538 let source =
539 DebugTypeIdx::from(u32::try_from(index).expect("invalid source type table index"));
540 let destination = DebugTypeIdx::from(
541 u32::try_from(type_offset + index).expect("too many types after merging"),
542 );
543 let inserted = remapping.types.push(destination).expect("too many remapped types");
544 debug_assert_eq!(inserted, source);
545 }
546 for (index, ty) in self.types.iter().enumerate() {
547 let source =
548 DebugTypeIdx::from(u32::try_from(index).expect("invalid source type table index"));
549 let ty = remap_debug_type_info(ty, &remapping.strings, &remapping.types)?;
550 let destination = target.push_type(ty);
551 debug_assert_eq!(destination, remapping.types[source]);
552 }
553
554 for (index, file) in self.files.iter().enumerate() {
555 let source =
556 DebugFileIdx::from(u32::try_from(index).expect("invalid source file table index"));
557 let path_idx = remapping.string(file.path_idx).ok_or(
558 DebugInfoTableRemapError::MissingSourceString { string_idx: file.path_idx },
559 )?;
560 let file = DebugFileInfo::new(path_idx)
561 .with_checksum(*file.checksum().unwrap_or(&DebugFileInfo::EMPTY_CHECKSUM));
562 let inserted = remapping
563 .files
564 .push(target.add_file_info(file))
565 .expect("too many remapped files");
566 debug_assert_eq!(inserted, source);
567 }
568
569 for (index, location) in self.locations.iter().enumerate() {
570 let source = DebugLocIdx::from(
571 u32::try_from(index).expect("invalid source location table index"),
572 );
573 let file_idx = remapping.file(location.file_idx).ok_or(
574 DebugInfoTableRemapError::MissingSourceFile { file_idx: location.file_idx },
575 )?;
576 let location = DebugLoc {
577 file_idx,
578 start: location.start,
579 end: location.end,
580 };
581 let inserted = remapping
582 .locations
583 .push(target.add_location_info(location))
584 .expect("too many remapped locations");
585 debug_assert_eq!(inserted, source);
586 }
587
588 for error_message in self.error_messages() {
589 let message = remapping.string(error_message.message).ok_or(
590 DebugInfoTableRemapError::MissingSourceString { string_idx: error_message.message },
591 )?;
592 target.add_error_message_with_index(error_message.err_code, message);
593 }
594
595 for (index, function) in self.functions().iter().enumerate() {
596 let source = DebugFunctionIdx::from(
597 u32::try_from(index).expect("invalid source function table index"),
598 );
599 let name_idx = remapping.string(function.name_idx).ok_or(
600 DebugInfoTableRemapError::MissingSourceString { string_idx: function.name_idx },
601 )?;
602 let linkage_name_idx = function
603 .linkage_name_idx
604 .try_into_option()
605 .map_err(|err| DebugInfoTableRemapError::InvalidOptionField {
606 context: "debug function linkage name",
607 err,
608 })?
609 .map(|index| {
610 remapping
611 .string(index)
612 .ok_or(DebugInfoTableRemapError::MissingSourceString { string_idx: index })
613 })
614 .transpose()?;
615 let type_idx = function
616 .type_idx
617 .try_into_option()
618 .map_err(|err| DebugInfoTableRemapError::InvalidOptionField {
619 context: "debug function type",
620 err,
621 })?
622 .map(|tid| {
623 remapping.ty(tid).ok_or(DebugInfoTableRemapError::MissingType { type_idx: tid })
624 })
625 .transpose()?;
626 let file_idx = remapping.file(function.file_idx).ok_or(
627 DebugInfoTableRemapError::MissingSourceFile { file_idx: function.file_idx },
628 )?;
629 let function_idx = target.add_function(FunctionInfo {
630 mast_root: function.mast_root,
631 source_node: None.into(),
632 type_idx: type_idx.into(),
633 linkage_name_idx: linkage_name_idx.into(),
634 name_idx,
635 file_idx,
636 line: function.line,
637 column: function.column,
638 });
639 let inserted =
640 remapping.functions.push(function_idx).expect("too many remapped functions");
641 debug_assert_eq!(inserted, source);
642 }
643
644 Ok(remapping)
645 }
646
647 fn validate_shared_table_references(&self) -> Result<(), DebugInfoTableRemapError> {
648 for ty in self.types.iter() {
649 validate_debug_type_info(ty, &self.strings, &self.types)?;
650 }
651 for file in self.files.iter() {
652 if self.strings.get(file.path_idx).is_none() {
653 return Err(DebugInfoTableRemapError::MissingSourceString {
654 string_idx: file.path_idx,
655 });
656 }
657 }
658 for location in self.locations.iter() {
659 if self.files.get(location.file_idx).is_none() {
660 return Err(DebugInfoTableRemapError::MissingSourceFile {
661 file_idx: location.file_idx,
662 });
663 }
664 }
665 for error_message in self.error_messages.iter() {
666 if self.strings.get(error_message.message).is_none() {
667 return Err(DebugInfoTableRemapError::MissingSourceString {
668 string_idx: error_message.message,
669 });
670 }
671 }
672 for function in self.functions.iter() {
673 if self.files.get(function.file_idx).is_none() {
674 return Err(DebugInfoTableRemapError::MissingSourceFile {
675 file_idx: function.file_idx,
676 });
677 }
678 if let Some(tid) = function.type_idx.try_into_option().map_err(|err| {
679 DebugInfoTableRemapError::InvalidOptionField { context: "debug function type", err }
680 })? && self.types.get(tid).is_none()
681 {
682 return Err(DebugInfoTableRemapError::MissingType { type_idx: tid });
683 }
684 if self.strings.get(function.name_idx).is_none() {
685 return Err(DebugInfoTableRemapError::MissingSourceString {
686 string_idx: function.name_idx,
687 });
688 }
689 if let Some(linkage_name_idx) =
690 function.linkage_name_idx.try_into_option().map_err(|err| {
691 DebugInfoTableRemapError::InvalidOptionField {
692 context: "debug function linkage name",
693 err,
694 }
695 })?
696 && self.strings.get(linkage_name_idx).is_none()
697 {
698 return Err(DebugInfoTableRemapError::MissingSourceString {
699 string_idx: linkage_name_idx,
700 });
701 }
702 }
703 Ok(())
704 }
705}
706
707impl<Src: SourceNodeIdMarker> DebugInfo<MastNodeId, Src> {
708 pub fn merge_source_debug<'a>(
717 inputs: impl IntoIterator<Item = (usize, &'a Self)>,
718 root_map: &MastForestRootMap,
719 ) -> Result<Self, DebugInfoMergeError<MastNodeId, Src>>
720 where
721 Src: 'a,
722 {
723 let mut builder = DebugInfoBuilder::default();
724 for (forest_index, debug_info) in inputs {
725 let tables = debug_info
726 .merge_tables_into(&mut builder)
727 .map_err(|error| table_remap_error(forest_index, error))?;
728
729 let mut remapped_nodes = FxHashMap::<Src, Src>::default();
730 let start_node_index = builder.debug_info().nodes().len();
731 for i in 0..debug_info.nodes.len() {
732 let prev_index = Src::from(u32::try_from(i).expect("too many nodes"));
733 let new_index = Src::from(
734 u32::try_from(start_node_index + i).expect("too many nodes after merging"),
735 );
736 remapped_nodes.insert(prev_index, new_index);
737 }
738
739 for (i, source_node) in debug_info.nodes.iter().enumerate() {
740 let prev_index = Src::from(u32::try_from(i).expect("too many nodes"));
741
742 let exec_node = root_map.map_node(forest_index, &source_node.exec_node).ok_or(
743 DebugInfoMergeError::MissingExecNodeMapping {
744 forest_index,
745 exec_node: source_node.exec_node,
746 },
747 )?;
748 let children = source_node
749 .children
750 .iter()
751 .map(|child| {
752 remapped_nodes.get(child).copied().ok_or(
753 DebugInfoMergeError::MissingSourceNodeMapping {
754 forest_index,
755 source_node: *child,
756 },
757 )
758 })
759 .collect::<Result<Vec<_>, _>>()?;
760
761 let mut asm_ops = Vec::with_capacity(source_node.asm_ops.len());
762 for row in source_node.asm_ops.iter() {
763 let location_idx = row
764 .location_idx
765 .try_into_option()
766 .map_err(|err| DebugInfoMergeError::InvalidOptionField {
767 forest_index,
768 context: "debug source assembly op location",
769 err,
770 })?
771 .map(|location_idx| {
772 tables.location(location_idx).ok_or(
773 DebugInfoMergeError::MissingSourceLocationMapping {
774 forest_index,
775 location_idx,
776 },
777 )
778 })
779 .transpose()?;
780 let context_name_idx = tables.string(row.context_name_idx).ok_or(
781 DebugInfoMergeError::MissingSourceStringMapping {
782 forest_index,
783 string_idx: row.context_name_idx,
784 },
785 )?;
786 let op_name_idx = tables.string(row.op_name_idx).ok_or(
787 DebugInfoMergeError::MissingSourceStringMapping {
788 forest_index,
789 string_idx: row.op_name_idx,
790 },
791 )?;
792 asm_ops.push(DebugSourceAsmOp::new(
793 row.op_idx,
794 location_idx,
795 context_name_idx,
796 op_name_idx,
797 row.num_cycles,
798 ));
799 }
800
801 let mut debug_vars = Vec::with_capacity(source_node.debug_vars.len());
802 for row in source_node.debug_vars.iter() {
803 let name_idx = tables.string(row.name_idx).ok_or(
804 DebugInfoMergeError::MissingSourceStringMapping {
805 forest_index,
806 string_idx: row.name_idx,
807 },
808 )?;
809
810 let location_idx = row
811 .location_idx
812 .map(|idx| {
813 tables.location(idx).ok_or(
814 DebugInfoMergeError::MissingSourceLocationMapping {
815 forest_index,
816 location_idx: idx,
817 },
818 )
819 })
820 .transpose()?;
821 let type_id = row
822 .type_id
823 .map(|idx| {
824 tables.ty(idx).ok_or(DebugInfoMergeError::MissingTypeMapping {
825 forest_index,
826 type_idx: idx,
827 })
828 })
829 .transpose()?;
830 debug_vars.push(DebugSourceVar {
831 op_idx: row.op_idx,
832 name_idx,
833 type_id,
834 arg_idx: row.arg_idx,
835 location_idx,
836 value_location: row.value_location.clone(),
837 });
838 }
839 let new_index = builder
840 .debug_info_mut()
841 .nodes
842 .push(SourceNode {
843 exec_node,
844 children,
845 op_start: source_node.op_start,
846 op_end: source_node.op_end,
847 asm_ops,
848 debug_vars,
849 inline_calls: Vec::with_capacity(source_node.inline_calls.len()),
850 })
851 .expect("too many nodes");
852 debug_assert_eq!(new_index, remapped_nodes[&prev_index],);
853 }
854
855 for (index, function) in debug_info.functions().iter().enumerate() {
856 let Some(previous_source_node) =
857 function.source_node.try_into_option().map_err(|err| {
858 DebugInfoMergeError::InvalidOptionField {
859 forest_index,
860 context: "debug function source node",
861 err,
862 }
863 })?
864 else {
865 continue;
866 };
867 let source_node = remapped_nodes.get(&previous_source_node).copied().ok_or(
868 DebugInfoMergeError::MissingSourceNodeMapping {
869 forest_index,
870 source_node: previous_source_node,
871 },
872 )?;
873 let previous_function = DebugFunctionIdx::from(
874 u32::try_from(index).expect("invalid source function table index"),
875 );
876 let function = tables.function(previous_function).ok_or(
877 DebugInfoMergeError::MissingFunctionMapping {
878 forest_index,
879 function_idx: previous_function,
880 },
881 )?;
882 builder.set_function_source_node(function, source_node);
883 }
884
885 for root in debug_info.roots().iter().copied() {
886 builder.debug_info_mut().roots.push(remapped_nodes.get(&root).copied().ok_or(
887 DebugInfoMergeError::MissingSourceNodeMapping {
888 forest_index,
889 source_node: root,
890 },
891 )?);
892 }
893
894 for (prev, new) in remapped_nodes.iter() {
895 let source_node = debug_info.source_node(*prev).unwrap();
896 if source_node.inline_calls.is_empty() {
897 continue;
898 }
899 let target_node = &mut builder[*new];
900 for row in source_node.inline_calls.iter() {
901 let callee_idx = tables.function(row.callee_idx).ok_or(
902 DebugInfoMergeError::MissingFunctionMapping {
903 forest_index,
904 function_idx: row.callee_idx,
905 },
906 )?;
907 let loc_idx = tables.location(row.loc_idx).ok_or(
908 DebugInfoMergeError::MissingSourceLocationMapping {
909 forest_index,
910 location_idx: row.loc_idx,
911 },
912 )?;
913 target_node.inline_calls.push(DebugSourceInlineCall {
914 op_idx: row.op_idx,
915 callee_idx,
916 loc_idx,
917 });
918 }
919 }
920 }
921
922 Ok(*builder.build())
923 }
924}
925
926fn validate_debug_type_info(
927 ty: &DebugTypeInfo,
928 strings: &IndexVec<DebugStringIdx, Arc<str>>,
929 types: &IndexVec<DebugTypeIdx, DebugTypeInfo>,
930) -> Result<(), DebugInfoTableRemapError> {
931 match ty {
932 DebugTypeInfo::Primitive(_) | DebugTypeInfo::Unknown => {},
933 DebugTypeInfo::Pointer { pointee_type_idx } => {
934 validate_type_idx(*pointee_type_idx, types)?;
935 },
936 DebugTypeInfo::Array { element_type_idx, .. } => {
937 validate_type_idx(*element_type_idx, types)?;
938 },
939 DebugTypeInfo::Struct { name_idx, fields, .. } => {
940 validate_type_string(*name_idx, strings)?;
941 for field in fields {
942 validate_type_string(field.name_idx, strings)?;
943 validate_type_idx(field.type_idx, types)?;
944 }
945 },
946 DebugTypeInfo::Function { return_type_idx, param_type_indices } => {
947 if let Some(return_type_idx) = return_type_idx {
948 validate_type_idx(*return_type_idx, types)?;
949 }
950 for ¶m_type_idx in param_type_indices {
951 validate_type_idx(param_type_idx, types)?;
952 }
953 },
954 DebugTypeInfo::Enum {
955 name_idx,
956 discriminant_type_idx,
957 variants,
958 ..
959 } => {
960 validate_type_string(*name_idx, strings)?;
961 validate_type_idx(*discriminant_type_idx, types)?;
962 for variant in variants {
963 validate_type_string(variant.name_idx, strings)?;
964 if let Some(type_idx) = variant.type_idx {
965 validate_type_idx(type_idx, types)?;
966 }
967 }
968 },
969 }
970 Ok(())
971}
972
973fn validate_type_string(
974 string_idx: DebugStringIdx,
975 strings: &IndexVec<DebugStringIdx, Arc<str>>,
976) -> Result<(), DebugInfoTableRemapError> {
977 if strings.get(string_idx).is_none() {
978 Err(DebugInfoTableRemapError::MissingTypeString { string_idx })
979 } else {
980 Ok(())
981 }
982}
983
984fn validate_type_idx(
985 type_idx: DebugTypeIdx,
986 types: &IndexVec<DebugTypeIdx, DebugTypeInfo>,
987) -> Result<(), DebugInfoTableRemapError> {
988 if types.get(type_idx).is_none() {
989 Err(DebugInfoTableRemapError::MissingType { type_idx })
990 } else {
991 Ok(())
992 }
993}
994
995fn remap_debug_type_info(
996 ty: &DebugTypeInfo,
997 string_map: &IndexVec<DebugStringIdx, DebugStringIdx>,
998 type_map: &IndexVec<DebugTypeIdx, DebugTypeIdx>,
999) -> Result<DebugTypeInfo, DebugInfoTableRemapError> {
1000 Ok(match ty {
1001 DebugTypeInfo::Primitive(primitive) => DebugTypeInfo::Primitive(*primitive),
1002 DebugTypeInfo::Pointer { pointee_type_idx } => DebugTypeInfo::Pointer {
1003 pointee_type_idx: remap_type_idx(*pointee_type_idx, type_map)?,
1004 },
1005 DebugTypeInfo::Array { element_type_idx, count } => DebugTypeInfo::Array {
1006 element_type_idx: remap_type_idx(*element_type_idx, type_map)?,
1007 count: *count,
1008 },
1009 DebugTypeInfo::Struct { name_idx, size, fields } => DebugTypeInfo::Struct {
1010 name_idx: remap_type_string(*name_idx, string_map)?,
1011 size: *size,
1012 fields: fields
1013 .iter()
1014 .map(|field| {
1015 Ok(DebugFieldInfo {
1016 name_idx: remap_type_string(field.name_idx, string_map)?,
1017 type_idx: remap_type_idx(field.type_idx, type_map)?,
1018 offset: field.offset,
1019 })
1020 })
1021 .collect::<Result<_, DebugInfoTableRemapError>>()?,
1022 },
1023 DebugTypeInfo::Function { return_type_idx, param_type_indices } => {
1024 DebugTypeInfo::Function {
1025 return_type_idx: return_type_idx
1026 .map(|idx| remap_type_idx(idx, type_map))
1027 .transpose()?,
1028 param_type_indices: param_type_indices
1029 .iter()
1030 .map(|idx| remap_type_idx(*idx, type_map))
1031 .collect::<Result<_, _>>()?,
1032 }
1033 },
1034 DebugTypeInfo::Enum {
1035 name_idx,
1036 size,
1037 discriminant_type_idx,
1038 variants,
1039 } => DebugTypeInfo::Enum {
1040 name_idx: remap_type_string(*name_idx, string_map)?,
1041 size: *size,
1042 discriminant_type_idx: remap_type_idx(*discriminant_type_idx, type_map)?,
1043 variants: variants
1044 .iter()
1045 .map(|variant| {
1046 Ok(DebugVariantInfo {
1047 name_idx: remap_type_string(variant.name_idx, string_map)?,
1048 type_idx: variant
1049 .type_idx
1050 .map(|idx| remap_type_idx(idx, type_map))
1051 .transpose()?,
1052 payload_offset: variant.payload_offset,
1053 discriminant: variant.discriminant,
1054 })
1055 })
1056 .collect::<Result<_, DebugInfoTableRemapError>>()?,
1057 },
1058 DebugTypeInfo::Unknown => DebugTypeInfo::Unknown,
1059 })
1060}
1061
1062fn remap_type_string(
1063 string_idx: DebugStringIdx,
1064 string_map: &IndexVec<DebugStringIdx, DebugStringIdx>,
1065) -> Result<DebugStringIdx, DebugInfoTableRemapError> {
1066 string_map
1067 .get(string_idx)
1068 .copied()
1069 .ok_or(DebugInfoTableRemapError::MissingTypeString { string_idx })
1070}
1071
1072fn remap_type_idx(
1073 type_idx: DebugTypeIdx,
1074 type_map: &IndexVec<DebugTypeIdx, DebugTypeIdx>,
1075) -> Result<DebugTypeIdx, DebugInfoTableRemapError> {
1076 type_map
1077 .get(type_idx)
1078 .copied()
1079 .ok_or(DebugInfoTableRemapError::MissingType { type_idx })
1080}
1081
1082fn table_remap_error<Exec: Idx, Src: Idx>(
1083 forest_index: usize,
1084 error: DebugInfoTableRemapError,
1085) -> DebugInfoMergeError<Exec, Src> {
1086 match error {
1087 DebugInfoTableRemapError::InvalidOptionField { context, err } => {
1088 DebugInfoMergeError::InvalidOptionField { forest_index, context, err }
1089 },
1090 DebugInfoTableRemapError::MissingTypeString { string_idx } => {
1091 DebugInfoMergeError::MissingTypeStringMapping { forest_index, string_idx }
1092 },
1093 DebugInfoTableRemapError::MissingType { type_idx } => {
1094 DebugInfoMergeError::MissingTypeMapping { forest_index, type_idx }
1095 },
1096 DebugInfoTableRemapError::MissingSourceString { string_idx } => {
1097 DebugInfoMergeError::MissingSourceStringMapping { forest_index, string_idx }
1098 },
1099 DebugInfoTableRemapError::MissingSourceFile { file_idx } => {
1100 DebugInfoMergeError::MissingSourceFileMapping { forest_index, file_idx }
1101 },
1102 }
1103}