1use std::fmt;
2use std::mem;
3use std::slice;
4
5use scroll::{ctx::TryFromCtx, Endian, Pread};
6
7use crate::common::*;
8use crate::modi::{
9 constants, CrossModuleExport, CrossModuleRef, FileChecksum, FileIndex, FileInfo, LineInfo,
10 LineInfoKind, ModuleRef,
11};
12use crate::symbol::{BinaryAnnotation, BinaryAnnotationsIter, InlineSiteSymbol};
13use crate::FallibleIterator;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16#[repr(u32)]
17#[allow(unused)]
18enum DebugSubsectionKind {
19 Symbols = 0xf1,
21 Lines = 0xf2,
22 StringTable = 0xf3,
23 FileChecksums = 0xf4,
24 FrameData = 0xf5,
25 InlineeLines = 0xf6,
26 CrossScopeImports = 0xf7,
27 CrossScopeExports = 0xf8,
28
29 ILLines = 0xf9,
31 FuncMDTokenMap = 0xfa,
32 TypeMDTokenMap = 0xfb,
33 MergedAssemblyInput = 0xfc,
34
35 CoffSymbolRva = 0xfd,
36}
37
38impl DebugSubsectionKind {
39 fn parse(value: u32) -> Result<Option<Self>> {
40 if (0xf1..=0xfd).contains(&value) {
41 Ok(Some(unsafe { std::mem::transmute(value) }))
42 } else if value == constants::DEBUG_S_IGNORE {
43 Ok(None)
44 } else {
45 Err(Error::UnimplementedDebugSubsection(value))
46 }
47 }
48}
49
50#[derive(Clone, Copy, Debug)]
51struct DebugSubsectionHeader {
52 kind: u32,
54 len: u32,
56}
57
58impl<'t> TryFromCtx<'t, Endian> for DebugSubsectionHeader {
59 type Error = scroll::Error;
60
61 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
62 let mut offset = 0;
63 let data = Self {
64 kind: this.gread_with(&mut offset, le)?,
65 len: this.gread_with(&mut offset, le)?,
66 };
67 Ok((data, offset))
68 }
69}
70
71impl DebugSubsectionHeader {
72 fn kind(self) -> Result<Option<DebugSubsectionKind>> {
73 DebugSubsectionKind::parse(self.kind)
74 }
75
76 fn len(self) -> usize {
77 self.len as usize
78 }
79}
80
81#[derive(Clone, Copy, Debug)]
82struct DebugSubsection<'a> {
83 pub kind: DebugSubsectionKind,
84 pub data: &'a [u8],
85}
86
87#[derive(Clone, Debug, Default)]
88struct DebugSubsectionIterator<'a> {
89 buf: ParseBuffer<'a>,
90}
91
92impl<'a> DebugSubsectionIterator<'a> {
93 fn new(data: &'a [u8]) -> Self {
94 Self {
95 buf: ParseBuffer::from(data),
96 }
97 }
98}
99
100impl<'a> FallibleIterator for DebugSubsectionIterator<'a> {
101 type Item = DebugSubsection<'a>;
102 type Error = Error;
103
104 fn next(&mut self) -> Result<Option<Self::Item>> {
105 while !self.buf.is_empty() {
106 let header = self.buf.parse::<DebugSubsectionHeader>()?;
107 let data = self.buf.take(header.len())?;
108 let kind = match header.kind()? {
109 Some(kind) => kind,
110 None => continue,
111 };
112
113 return Ok(Some(DebugSubsection { kind, data }));
114 }
115
116 Ok(None)
117 }
118}
119
120#[derive(Clone, Copy, Debug, Default)]
121struct DebugInlineeLinesHeader {
122 signature: u32,
124}
125
126impl<'t> TryFromCtx<'t, Endian> for DebugInlineeLinesHeader {
127 type Error = scroll::Error;
128
129 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
130 let mut offset = 0;
131 let data = Self {
132 signature: this.gread_with(&mut offset, le)?,
133 };
134 Ok((data, offset))
135 }
136}
137
138impl DebugInlineeLinesHeader {
139 pub fn has_extra_files(self) -> bool {
140 self.signature == constants::CV_INLINEE_SOURCE_LINE_SIGNATURE_EX
141 }
142}
143
144#[derive(Clone, Copy, Debug, Default, PartialEq)]
145pub struct InlineeSourceLine<'a> {
146 pub inlinee: IdIndex,
147 pub file_id: FileIndex,
148 pub line: u32,
149 extra_files: &'a [u8],
150}
151
152impl<'a> InlineeSourceLine<'a> {
153 }
155
156impl<'a> TryFromCtx<'a, DebugInlineeLinesHeader> for InlineeSourceLine<'a> {
157 type Error = Error;
158
159 fn try_from_ctx(this: &'a [u8], header: DebugInlineeLinesHeader) -> Result<(Self, usize)> {
160 let mut buf = ParseBuffer::from(this);
161 let inlinee = buf.parse()?;
162 let file_id = buf.parse()?;
163 let line = buf.parse()?;
164
165 let extra_files = if header.has_extra_files() {
166 let file_count = buf.parse::<u32>()? as usize;
167 buf.take(file_count * std::mem::size_of::<u32>())?
168 } else {
169 &[]
170 };
171
172 let source_line = Self {
173 inlinee,
174 file_id,
175 line,
176 extra_files,
177 };
178
179 Ok((source_line, buf.pos()))
180 }
181}
182
183#[derive(Debug, Clone, Default)]
184struct DebugInlineeLinesIterator<'a> {
185 header: DebugInlineeLinesHeader,
186 buf: ParseBuffer<'a>,
187}
188
189impl<'a> FallibleIterator for DebugInlineeLinesIterator<'a> {
190 type Item = InlineeSourceLine<'a>;
191 type Error = Error;
192
193 fn next(&mut self) -> Result<Option<Self::Item>> {
194 if self.buf.is_empty() {
195 Ok(None)
196 } else {
197 Ok(Some(self.buf.parse_with(self.header)?))
198 }
199 }
200}
201
202#[derive(Clone, Debug, Default)]
203struct DebugInlineeLinesSubsection<'a> {
204 header: DebugInlineeLinesHeader,
205 data: &'a [u8],
206}
207
208impl<'a> DebugInlineeLinesSubsection<'a> {
209 fn parse(data: &'a [u8]) -> Result<Self> {
210 let mut buf = ParseBuffer::from(data);
211 let header = buf.parse::<DebugInlineeLinesHeader>()?;
212
213 Ok(Self {
214 header,
215 data: &data[buf.pos()..],
216 })
217 }
218
219 fn lines(&self) -> DebugInlineeLinesIterator<'a> {
221 DebugInlineeLinesIterator {
222 header: self.header,
223 buf: ParseBuffer::from(self.data),
224 }
225 }
226}
227
228#[derive(Clone, Copy, Debug, Default)]
229struct DebugLinesHeader {
230 offset: PdbInternalSectionOffset,
232 flags: u16,
234 code_size: u32,
236}
237
238impl<'t> TryFromCtx<'t, Endian> for DebugLinesHeader {
239 type Error = scroll::Error;
240
241 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
242 let mut offset = 0;
243 let data = Self {
244 offset: this.gread_with(&mut offset, le)?,
245 flags: this.gread_with(&mut offset, le)?,
246 code_size: this.gread_with(&mut offset, le)?,
247 };
248 Ok((data, offset))
249 }
250}
251
252impl DebugLinesHeader {
253 fn has_columns(self) -> bool {
254 self.flags & constants::CV_LINES_HAVE_COLUMNS != 0
255 }
256}
257
258#[derive(Clone, Copy, Debug)]
259struct DebugLinesSubsection<'a> {
260 header: DebugLinesHeader,
261 data: &'a [u8],
262}
263
264impl<'a> DebugLinesSubsection<'a> {
265 fn parse(data: &'a [u8]) -> Result<Self> {
266 let mut buf = ParseBuffer::from(data);
267 let header = buf.parse()?;
268 let data = &data[buf.pos()..];
269 Ok(Self { header, data })
270 }
271
272 fn blocks(&self) -> DebugLinesBlockIterator<'a> {
273 DebugLinesBlockIterator {
274 header: self.header,
275 buf: ParseBuffer::from(self.data),
276 }
277 }
278}
279
280#[derive(Clone, Copy, Debug, Eq, PartialEq)]
282enum LineMarkerKind {
283 DoNotStepOnto,
285 DoNotStepInto,
287}
288
289#[repr(C)]
291#[derive(Clone, Copy, Debug)]
292struct LineNumberHeader {
293 offset: u32,
295 flags: u32,
303}
304
305impl<'t> TryFromCtx<'t, Endian> for LineNumberHeader {
306 type Error = scroll::Error;
307
308 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
309 let mut offset = 0;
310 let data = Self {
311 offset: this.gread_with(&mut offset, le)?,
312 flags: this.gread_with(&mut offset, le)?,
313 };
314 Ok((data, offset))
315 }
316}
317
318#[derive(Clone, Debug)]
320struct LineNumberEntry {
321 pub offset: u32,
323 pub start_line: u32,
325 pub end_line: u32,
327 pub kind: LineInfoKind,
329}
330
331#[derive(Clone, Debug)]
333struct LineMarkerEntry {
334 #[allow(dead_code)] pub offset: u32,
337 #[allow(dead_code)] pub kind: LineMarkerKind,
340}
341
342#[derive(Clone, Debug)]
344enum LineEntry {
345 Number(LineNumberEntry),
347 Marker(LineMarkerEntry),
349}
350
351impl LineNumberHeader {
352 pub fn parse(self) -> LineEntry {
354 let start_line = self.flags & 0x00ff_ffff;
357 let marker = match start_line {
358 0xfeefee => Some(LineMarkerKind::DoNotStepOnto),
359 0xf00f00 => Some(LineMarkerKind::DoNotStepInto),
360 _ => None,
361 };
362
363 if let Some(kind) = marker {
364 return LineEntry::Marker(LineMarkerEntry {
365 offset: self.offset,
366 kind,
367 });
368 }
369
370 let line_delta = self.flags & 0x7f00_0000 >> 24;
374
375 let high_start = start_line & !0x7f;
379 let mut end_line = high_start | line_delta;
380
381 if end_line < start_line {
385 end_line += 1 << 7;
386 }
387
388 let kind = if self.flags & 0x8000_0000 != 0 {
389 LineInfoKind::Statement
390 } else {
391 LineInfoKind::Expression
392 };
393
394 LineEntry::Number(LineNumberEntry {
395 offset: self.offset,
396 start_line,
397 end_line,
398 kind,
399 })
400 }
401}
402
403#[derive(Clone, Debug, Default)]
404struct DebugLinesIterator<'a> {
405 block: DebugLinesBlockHeader,
406 buf: ParseBuffer<'a>,
407}
408
409impl FallibleIterator for DebugLinesIterator<'_> {
410 type Item = LineEntry;
411 type Error = Error;
412
413 fn next(&mut self) -> Result<Option<Self::Item>> {
414 if self.buf.is_empty() {
415 return Ok(None);
416 }
417
418 self.buf.parse().map(LineNumberHeader::parse).map(Some)
419 }
420}
421
422#[repr(C)]
423#[derive(Clone, Copy, Debug, Default)]
424struct ColumnNumberEntry {
425 start_column: u16,
426 end_column: u16,
427}
428
429impl<'t> TryFromCtx<'t, Endian> for ColumnNumberEntry {
430 type Error = scroll::Error;
431
432 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
433 let mut offset = 0;
434 let data = Self {
435 start_column: this.gread_with(&mut offset, le)?,
436 end_column: this.gread_with(&mut offset, le)?,
437 };
438 Ok((data, offset))
439 }
440}
441
442#[derive(Clone, Debug, Default)]
443struct DebugColumnsIterator<'a> {
444 buf: ParseBuffer<'a>,
445}
446
447impl FallibleIterator for DebugColumnsIterator<'_> {
448 type Item = ColumnNumberEntry;
449 type Error = Error;
450
451 fn next(&mut self) -> Result<Option<Self::Item>> {
452 if self.buf.is_empty() {
453 return Ok(None);
454 }
455
456 self.buf.parse().map(Some)
457 }
458}
459
460#[repr(C)]
461#[derive(Clone, Copy, Debug, Default)]
462struct DebugLinesBlockHeader {
463 file_index: u32,
465
466 num_lines: u32,
471
472 block_size: u32,
474}
475
476impl<'t> TryFromCtx<'t, Endian> for DebugLinesBlockHeader {
477 type Error = scroll::Error;
478
479 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
480 let mut offset = 0;
481 let data = Self {
482 file_index: this.gread_with(&mut offset, le)?,
483 num_lines: this.gread_with(&mut offset, le)?,
484 block_size: this.gread_with(&mut offset, le)?,
485 };
486 Ok((data, offset))
487 }
488}
489
490impl DebugLinesBlockHeader {
491 fn data_size(&self) -> usize {
493 self.block_size as usize - std::mem::size_of::<Self>()
494 }
495
496 fn line_size(&self) -> usize {
498 self.num_lines as usize * std::mem::size_of::<LineNumberHeader>()
499 }
500
501 fn column_size(&self, subsection: DebugLinesHeader) -> usize {
503 if subsection.has_columns() {
504 self.num_lines as usize * std::mem::size_of::<ColumnNumberEntry>()
505 } else {
506 0
507 }
508 }
509}
510
511#[derive(Clone, Debug)]
512struct DebugLinesBlock<'a> {
513 header: DebugLinesBlockHeader,
514 line_data: &'a [u8],
515 column_data: &'a [u8],
516}
517
518impl<'a> DebugLinesBlock<'a> {
519 #[allow(unused)]
520 fn file_index(&self) -> FileIndex {
521 FileIndex(self.header.file_index)
522 }
523
524 fn lines(&self) -> DebugLinesIterator<'a> {
525 DebugLinesIterator {
526 block: self.header,
527 buf: ParseBuffer::from(self.line_data),
528 }
529 }
530
531 fn columns(&self) -> DebugColumnsIterator<'a> {
532 DebugColumnsIterator {
533 buf: ParseBuffer::from(self.column_data),
534 }
535 }
536}
537
538#[derive(Clone, Debug, Default)]
539struct DebugLinesBlockIterator<'a> {
540 header: DebugLinesHeader,
541 buf: ParseBuffer<'a>,
542}
543
544impl<'a> FallibleIterator for DebugLinesBlockIterator<'a> {
545 type Item = DebugLinesBlock<'a>;
546 type Error = Error;
547
548 fn next(&mut self) -> Result<Option<Self::Item>> {
549 if self.buf.is_empty() {
550 return Ok(None);
551 }
552
553 let header = self.buf.parse::<DebugLinesBlockHeader>()?;
557 let data = self.buf.take(header.data_size())?;
558
559 let (line_data, data) = data.split_at(header.line_size());
562 let (column_data, remainder) = data.split_at(header.column_size(self.header));
563
564 debug_assert!(remainder.is_empty());
566
567 Ok(Some(DebugLinesBlock {
568 header,
569 line_data,
570 column_data,
571 }))
572 }
573}
574
575#[repr(u8)]
577#[allow(unused)]
578#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
579enum FileChecksumKind {
580 None = 0,
581 Md5 = 1,
582 Sha1 = 2,
583 Sha256 = 3,
584}
585
586impl FileChecksumKind {
587 fn parse(value: u8) -> Result<Self> {
589 if value <= 3 {
590 Ok(unsafe { std::mem::transmute(value) })
591 } else {
592 Err(Error::UnimplementedFileChecksumKind(value))
593 }
594 }
595}
596
597#[derive(Clone, Copy, Debug)]
599struct FileChecksumHeader {
600 name_offset: u32,
601 checksum_size: u8,
602 checksum_kind: u8,
603}
604
605impl<'t> TryFromCtx<'t, Endian> for FileChecksumHeader {
606 type Error = scroll::Error;
607
608 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
609 let mut offset = 0;
610 let data = Self {
611 name_offset: this.gread_with(&mut offset, le)?,
612 checksum_size: this.gread_with(&mut offset, le)?,
613 checksum_kind: this.gread_with(&mut offset, le)?,
614 };
615 Ok((data, offset))
616 }
617}
618
619#[derive(Clone, Debug)]
621struct FileChecksumEntry<'a> {
622 name: StringRef,
624 checksum: FileChecksum<'a>,
626}
627
628#[derive(Clone, Debug, Default)]
629struct DebugFileChecksumsIterator<'a> {
630 buf: ParseBuffer<'a>,
631}
632
633impl<'a> FallibleIterator for DebugFileChecksumsIterator<'a> {
634 type Item = FileChecksumEntry<'a>;
635 type Error = Error;
636
637 fn next(&mut self) -> Result<Option<Self::Item>> {
638 if self.buf.is_empty() {
639 return Ok(None);
640 }
641
642 let header = self.buf.parse::<FileChecksumHeader>()?;
643 let checksum_data = self.buf.take(header.checksum_size as usize)?;
644
645 let checksum = match FileChecksumKind::parse(header.checksum_kind)? {
646 FileChecksumKind::None => FileChecksum::None,
647 FileChecksumKind::Md5 => FileChecksum::Md5(checksum_data),
648 FileChecksumKind::Sha1 => FileChecksum::Sha1(checksum_data),
649 FileChecksumKind::Sha256 => FileChecksum::Sha256(checksum_data),
650 };
651
652 self.buf.align(4)?;
653
654 Ok(Some(FileChecksumEntry {
655 name: StringRef(header.name_offset),
656 checksum,
657 }))
658 }
659}
660
661#[derive(Clone, Debug, Default)]
662struct DebugFileChecksumsSubsection<'a> {
663 data: &'a [u8],
664}
665
666impl<'a> DebugFileChecksumsSubsection<'a> {
667 fn new(data: &'a [u8]) -> Self {
669 Self { data }
670 }
671
672 #[allow(unused)]
674 fn entries(&self) -> Result<DebugFileChecksumsIterator<'a>> {
675 self.entries_at_offset(FileIndex(0))
676 }
677
678 fn entries_at_offset(&self, offset: FileIndex) -> Result<DebugFileChecksumsIterator<'a>> {
680 let mut buf = ParseBuffer::from(self.data);
681 buf.take(offset.0 as usize)?;
682 Ok(DebugFileChecksumsIterator { buf })
683 }
684}
685
686#[derive(Clone, Copy, Debug)]
687struct CrossScopeImportModule<'a> {
688 name: ModuleRef,
689 imports: &'a [u32],
691}
692
693impl CrossScopeImportModule<'_> {
694 fn get<I>(self, import: usize) -> Option<Local<I>>
700 where
701 I: ItemIndex,
702 {
703 let value = self.imports.get(import)?;
704 let index = u32::from_le(*value).into();
705 Some(Local(index))
706 }
707}
708
709#[derive(Clone, Debug, Default)]
710struct CrossScopeImportModuleIter<'a> {
711 buf: ParseBuffer<'a>,
712}
713
714impl<'a> FallibleIterator for CrossScopeImportModuleIter<'a> {
715 type Item = CrossScopeImportModule<'a>;
716 type Error = Error;
717
718 fn next(&mut self) -> Result<Option<Self::Item>> {
719 if self.buf.is_empty() {
720 return Ok(None);
721 }
722
723 let name = ModuleRef(self.buf.parse()?);
724 let count = self.buf.parse::<u32>()? as usize;
725
726 let data = self.buf.take(count * mem::size_of::<u32>())?;
727 let imports = cast_aligned(data).ok_or(Error::InvalidStreamLength("CrossScopeImports"))?;
728
729 Ok(Some(CrossScopeImportModule { name, imports }))
730 }
731}
732
733#[derive(Clone, Copy, Debug, Default)]
734struct DebugCrossScopeImportsSubsection<'a> {
735 data: &'a [u8],
736}
737
738impl<'a> DebugCrossScopeImportsSubsection<'a> {
739 fn new(data: &'a [u8]) -> Self {
740 Self { data }
741 }
742
743 fn modules(self) -> CrossScopeImportModuleIter<'a> {
744 let buf = ParseBuffer::from(self.data);
745 CrossScopeImportModuleIter { buf }
746 }
747}
748
749#[derive(Clone, Debug, Default)]
754pub struct CrossModuleImports<'a> {
755 modules: Vec<CrossScopeImportModule<'a>>,
756}
757
758impl<'a> CrossModuleImports<'a> {
759 fn from_section(section: DebugCrossScopeImportsSubsection<'a>) -> Result<Self> {
761 let modules = section.modules().collect()?;
762 Ok(Self { modules })
763 }
764
765 pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
767 let import_data = DebugSubsectionIterator::new(data)
768 .find(|sec| Ok(sec.kind == DebugSubsectionKind::CrossScopeImports))?
769 .map(|sec| sec.data);
770
771 match import_data {
772 Some(d) => Self::from_section(DebugCrossScopeImportsSubsection::new(d)),
773 None => Ok(Self::default()),
774 }
775 }
776
777 pub fn resolve_import<I>(&self, index: I) -> Result<CrossModuleRef<I>>
790 where
791 I: ItemIndex,
792 {
793 let raw_index = index.into();
794 if !index.is_cross_module() {
795 return Err(Error::NotACrossModuleRef(raw_index));
796 }
797
798 let module_index = ((raw_index >> 20) & 0x7ff) as usize;
799 let import_index = (raw_index & 0x000f_ffff) as usize;
800
801 let module = self
802 .modules
803 .get(module_index)
804 .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
805
806 let local_index = module
807 .get(import_index)
808 .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
809
810 Ok(CrossModuleRef(module.name, local_index))
811 }
812}
813
814#[repr(C)]
822#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
823struct RawCrossScopeExport {
824 local: u32,
828
829 global: u32,
833}
834
835impl<'t> TryFromCtx<'t, Endian> for RawCrossScopeExport {
836 type Error = scroll::Error;
837
838 fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
839 let mut offset = 0;
840 let data = Self {
841 local: this.gread_with(&mut offset, le)?,
842 global: this.gread_with(&mut offset, le)?,
843 };
844 Ok((data, offset))
845 }
846}
847
848impl From<RawCrossScopeExport> for CrossModuleExport {
849 fn from(raw: RawCrossScopeExport) -> Self {
850 if (raw.local & 0x8000_0000) != 0 {
851 Self::Id(Local(IdIndex(raw.local)), IdIndex(raw.global))
852 } else {
853 Self::Type(Local(TypeIndex(raw.local)), TypeIndex(raw.global))
854 }
855 }
856}
857
858struct RawCrossScopeExportsIter<'a> {
859 buf: ParseBuffer<'a>,
860}
861
862impl FallibleIterator for RawCrossScopeExportsIter<'_> {
863 type Item = RawCrossScopeExport;
864 type Error = Error;
865
866 fn next(&mut self) -> Result<Option<Self::Item>> {
867 if self.buf.is_empty() {
868 return Ok(None);
869 }
870
871 self.buf.parse().map(Some)
872 }
873}
874
875#[derive(Clone, Copy, Debug, Default)]
876struct DebugCrossScopeExportsSubsection<'a> {
877 data: &'a [u8],
878}
879
880impl<'a> DebugCrossScopeExportsSubsection<'a> {
881 fn parse(data: &'a [u8]) -> Result<Self> {
883 if cast_aligned::<RawCrossScopeExport>(data).is_none() {
884 return Err(Error::InvalidStreamLength(
885 "DebugCrossScopeExportsSubsection",
886 ));
887 }
888
889 Ok(Self { data })
890 }
891
892 fn exports(self) -> RawCrossScopeExportsIter<'a> {
893 let buf = ParseBuffer::from(self.data);
894 RawCrossScopeExportsIter { buf }
895 }
896}
897
898#[derive(Clone, Debug)]
900pub struct CrossModuleExportIter<'a> {
901 exports: slice::Iter<'a, RawCrossScopeExport>,
902}
903
904impl Default for CrossModuleExportIter<'_> {
905 fn default() -> Self {
906 Self { exports: [].iter() }
907 }
908}
909
910impl<'a> FallibleIterator for CrossModuleExportIter<'a> {
911 type Item = CrossModuleExport;
912 type Error = Error;
913
914 fn next(&mut self) -> Result<Option<Self::Item>> {
915 Ok(self.exports.next().map(|r| (*r).into()))
916 }
917}
918
919#[derive(Clone, Debug, Default)]
924pub struct CrossModuleExports {
925 raw_exports: Vec<RawCrossScopeExport>,
926}
927
928impl CrossModuleExports {
929 fn from_section(section: DebugCrossScopeExportsSubsection<'_>) -> Result<Self> {
930 let raw_exports = section.exports().collect()?;
931 Ok(Self { raw_exports })
932 }
933
934 pub(crate) fn parse(data: &[u8]) -> Result<Self> {
935 let export_data = DebugSubsectionIterator::new(data)
936 .find(|sec| Ok(sec.kind == DebugSubsectionKind::CrossScopeExports))?
937 .map(|sec| sec.data);
938
939 match export_data {
940 Some(d) => Self::from_section(DebugCrossScopeExportsSubsection::parse(d)?),
941 None => Ok(Self::default()),
942 }
943 }
944
945 #[inline]
947 pub fn len(&self) -> usize {
948 self.raw_exports.len()
949 }
950
951 #[inline]
953 pub fn is_empty(&self) -> bool {
954 self.raw_exports.is_empty()
955 }
956
957 pub fn exports(&self) -> CrossModuleExportIter<'_> {
959 CrossModuleExportIter {
960 exports: self.raw_exports.iter(),
961 }
962 }
963
964 pub fn resolve_import<I>(&self, local_index: Local<I>) -> Result<Option<I>>
971 where
972 I: ItemIndex,
973 {
974 let local = local_index.0.into();
975 let exports = &self.raw_exports;
976
977 Ok(match exports.binary_search_by_key(&local, |r| r.local) {
978 Ok(i) => Some(I::from(exports[i].global)),
979 Err(_) => None,
980 })
981 }
982}
983
984#[derive(Clone)]
985pub struct LineIterator<'a> {
986 sections: std::slice::Iter<'a, DebugLinesSubsection<'a>>,
988 blocks: DebugLinesBlockIterator<'a>,
990 lines: DebugLinesIterator<'a>,
992 columns: DebugColumnsIterator<'a>,
994 last_info: Option<LineInfo>,
996}
997
998impl<'a> FallibleIterator for LineIterator<'a> {
999 type Item = LineInfo;
1000 type Error = Error;
1001
1002 fn next(&mut self) -> Result<Option<Self::Item>> {
1003 loop {
1004 if let Some(entry) = self.lines.next()? {
1005 let column_entry = self.columns.next()?;
1010
1011 let line_entry = match entry {
1015 LineEntry::Number(line_entry) => line_entry,
1016 LineEntry::Marker(_) => continue,
1017 };
1018
1019 let section_header = self.blocks.header;
1020 let block_header = self.lines.block;
1021
1022 let offset = section_header.offset + line_entry.offset;
1023
1024 let line_info = LineInfo {
1025 offset,
1026 length: None, file_index: FileIndex(block_header.file_index),
1028 line_start: line_entry.start_line,
1029 line_end: line_entry.end_line,
1030 column_start: column_entry.map(|e| e.start_column.into()),
1031 column_end: column_entry.map(|e| e.end_column.into()),
1032 kind: line_entry.kind,
1033 };
1034
1035 let mut last_info = match std::mem::replace(&mut self.last_info, Some(line_info)) {
1036 Some(last_info) => last_info,
1037 None => continue,
1038 };
1039
1040 last_info.set_end(offset);
1041 return Ok(Some(last_info));
1042 }
1043
1044 if let Some(block) = self.blocks.next()? {
1045 self.lines = block.lines();
1046 self.columns = block.columns();
1047 continue;
1048 }
1049
1050 if let Some(ref mut last_line) = self.last_info {
1054 let section_header = self.blocks.header;
1055 last_line.set_end(section_header.offset + section_header.code_size);
1056 }
1057
1058 if let Some(lines_section) = self.sections.next() {
1059 self.blocks = lines_section.blocks();
1060 continue;
1061 }
1062
1063 return Ok(self.last_info.take());
1064 }
1065 }
1066}
1067
1068impl Default for LineIterator<'_> {
1069 fn default() -> Self {
1070 Self {
1071 sections: [].iter(),
1072 blocks: DebugLinesBlockIterator::default(),
1073 lines: DebugLinesIterator::default(),
1074 columns: DebugColumnsIterator::default(),
1075 last_info: None,
1076 }
1077 }
1078}
1079
1080impl fmt::Debug for LineIterator<'_> {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 f.debug_struct("LineIterator")
1083 .field("sections", &self.sections.as_slice())
1084 .field("blocks", &self.blocks)
1085 .field("lines", &self.lines)
1086 .field("columns", &self.columns)
1087 .field("last_info", &self.last_info)
1088 .finish()
1089 }
1090}
1091
1092#[derive(Clone, Debug, Default)]
1094pub struct InlineeLineIterator<'a> {
1095 annotations: BinaryAnnotationsIter<'a>,
1096 file_index: FileIndex,
1097 code_offset_base: u32,
1098 code_offset: PdbInternalSectionOffset,
1099 code_length: Option<u32>,
1100 line: u32,
1101 line_length: u32,
1102 col_start: Option<u32>,
1103 col_end: Option<u32>,
1104 line_kind: LineInfoKind,
1105 last_info: Option<LineInfo>,
1106}
1107
1108impl<'a> InlineeLineIterator<'a> {
1109 fn new(
1110 parent_offset: PdbInternalSectionOffset,
1111 inline_site: &InlineSiteSymbol<'a>,
1112 inlinee_line: InlineeSourceLine<'a>,
1113 ) -> Self {
1114 Self {
1115 annotations: inline_site.annotations.iter(),
1116 file_index: inlinee_line.file_id,
1117 code_offset_base: 0,
1118 code_offset: parent_offset,
1119 code_length: None,
1120 line: inlinee_line.line,
1121 line_length: 1,
1122 col_start: None,
1123 col_end: None,
1124 line_kind: LineInfoKind::Statement,
1125 last_info: None,
1126 }
1127 }
1128}
1129
1130impl<'a> FallibleIterator for InlineeLineIterator<'a> {
1131 type Item = LineInfo;
1132 type Error = Error;
1133
1134 fn next(&mut self) -> Result<Option<Self::Item>> {
1135 while let Some(op) = self.annotations.next()? {
1136 match op {
1137 BinaryAnnotation::CodeOffset(code_offset) => {
1138 self.code_offset.offset = code_offset;
1139 }
1140 BinaryAnnotation::ChangeCodeOffsetBase(code_offset_base) => {
1141 self.code_offset_base = code_offset_base;
1142 }
1143 BinaryAnnotation::ChangeCodeOffset(delta) => {
1144 self.code_offset = self.code_offset.wrapping_add(delta);
1145 }
1146 BinaryAnnotation::ChangeCodeLength(code_length) => {
1147 if let Some(ref mut last_info) = self.last_info {
1148 if last_info.length.is_none() && last_info.kind == self.line_kind {
1149 last_info.length = Some(code_length);
1150 }
1151 }
1152
1153 self.code_offset = self.code_offset.wrapping_add(code_length);
1154 }
1155 BinaryAnnotation::ChangeFile(file_index) => {
1156 self.file_index = file_index;
1163 }
1164 BinaryAnnotation::ChangeLineOffset(delta) => {
1165 self.line = (i64::from(self.line) + i64::from(delta)) as u32;
1166 }
1167 BinaryAnnotation::ChangeLineEndDelta(line_length) => {
1168 self.line_length = line_length;
1169 }
1170 BinaryAnnotation::ChangeRangeKind(kind) => {
1171 self.line_kind = match kind {
1172 0 => LineInfoKind::Expression,
1173 1 => LineInfoKind::Statement,
1174 _ => self.line_kind,
1175 };
1176 }
1177 BinaryAnnotation::ChangeColumnStart(col_start) => {
1178 self.col_start = Some(col_start);
1179 }
1180 BinaryAnnotation::ChangeColumnEndDelta(delta) => {
1181 self.col_end = self
1182 .col_end
1183 .map(|col_end| (i64::from(col_end) + i64::from(delta)) as u32)
1184 }
1185 BinaryAnnotation::ChangeCodeOffsetAndLineOffset(code_delta, line_delta) => {
1186 self.code_offset += code_delta;
1187 self.line = (i64::from(self.line) + i64::from(line_delta)) as u32;
1188 }
1189 BinaryAnnotation::ChangeCodeLengthAndCodeOffset(code_length, code_delta) => {
1190 self.code_length = Some(code_length);
1191 self.code_offset += code_delta;
1192 }
1193 BinaryAnnotation::ChangeColumnEnd(col_end) => {
1194 self.col_end = Some(col_end);
1195 }
1196 }
1197
1198 if !op.emits_line_info() {
1199 continue;
1200 }
1201
1202 let line_offset = self.code_offset + self.code_offset_base;
1203 if let Some(ref mut last_info) = self.last_info {
1204 if last_info.length.is_none() && last_info.kind == self.line_kind {
1205 last_info.length = Some(line_offset.offset - last_info.offset.offset);
1206 }
1207 }
1208
1209 let line_info = LineInfo {
1210 kind: self.line_kind,
1211 file_index: self.file_index,
1212 offset: line_offset,
1213 length: self.code_length,
1214 line_start: self.line,
1215 line_end: self.line + self.line_length,
1216 column_start: self.col_start,
1217 column_end: self.col_end,
1218 };
1219
1220 self.code_length = None;
1222
1223 if let Some(last_info) = std::mem::replace(&mut self.last_info, Some(line_info)) {
1226 return Ok(Some(last_info));
1227 }
1228 }
1229
1230 Ok(self.last_info.take())
1231 }
1232}
1233
1234#[derive(Clone, Debug, Default)]
1236pub struct Inlinee<'a>(InlineeSourceLine<'a>);
1237
1238impl<'a> Inlinee<'a> {
1239 pub fn index(&self) -> IdIndex {
1241 self.0.inlinee
1242 }
1243
1244 pub fn lines(
1250 &self,
1251 parent_offset: PdbInternalSectionOffset,
1252 inline_site: &InlineSiteSymbol<'a>,
1253 ) -> InlineeLineIterator<'a> {
1254 InlineeLineIterator::new(parent_offset, inline_site, self.0)
1255 }
1256}
1257
1258#[derive(Clone, Debug, Default)]
1260pub struct InlineeIterator<'a> {
1261 inlinee_lines: DebugInlineeLinesIterator<'a>,
1262}
1263
1264impl<'a> InlineeIterator<'a> {
1265 pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
1266 let inlinee_data = DebugSubsectionIterator::new(data)
1267 .find(|sec| Ok(sec.kind == DebugSubsectionKind::InlineeLines))?
1268 .map(|sec| sec.data);
1269
1270 let inlinee_lines = match inlinee_data {
1271 Some(d) => DebugInlineeLinesSubsection::parse(d)?,
1272 None => DebugInlineeLinesSubsection::default(),
1273 };
1274
1275 Ok(Self {
1276 inlinee_lines: inlinee_lines.lines(),
1277 })
1278 }
1279}
1280
1281impl<'a> FallibleIterator for InlineeIterator<'a> {
1282 type Item = Inlinee<'a>;
1283 type Error = Error;
1284
1285 fn next(&mut self) -> Result<Option<Self::Item>> {
1286 match self.inlinee_lines.next() {
1287 Ok(Some(inlinee_line)) => Ok(Some(Inlinee(inlinee_line))),
1288 Ok(None) => Ok(None),
1289 Err(error) => Err(error),
1290 }
1291 }
1292}
1293
1294#[derive(Clone, Debug, Default)]
1295pub struct FileIterator<'a> {
1296 checksums: DebugFileChecksumsIterator<'a>,
1297}
1298
1299impl<'a> FallibleIterator for FileIterator<'a> {
1300 type Item = FileInfo<'a>;
1301 type Error = Error;
1302
1303 fn next(&mut self) -> Result<Option<Self::Item>> {
1304 match self.checksums.next() {
1305 Ok(Some(entry)) => Ok(Some(FileInfo {
1306 name: entry.name,
1307 checksum: entry.checksum,
1308 })),
1309 Ok(None) => Ok(None),
1310 Err(error) => Err(error),
1311 }
1312 }
1313}
1314
1315pub struct LineProgram<'a> {
1316 file_checksums: DebugFileChecksumsSubsection<'a>,
1317 line_sections: Vec<DebugLinesSubsection<'a>>,
1318}
1319
1320impl<'a> LineProgram<'a> {
1321 pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
1322 let mut file_checksums = DebugFileChecksumsSubsection::default();
1323 let mut line_sections = Vec::new();
1324
1325 let mut section_iter = DebugSubsectionIterator::new(data);
1326 while let Some(sec) = section_iter.next()? {
1327 match sec.kind {
1328 DebugSubsectionKind::FileChecksums => {
1329 file_checksums = DebugFileChecksumsSubsection::new(sec.data);
1330 }
1331 DebugSubsectionKind::Lines => {
1332 line_sections.push(DebugLinesSubsection::parse(sec.data)?);
1333 }
1334 _ => {}
1335 }
1336 }
1337
1338 line_sections.sort_unstable_by_key(Self::lines_key);
1339
1340 Ok(Self {
1341 file_checksums,
1342 line_sections,
1343 })
1344 }
1345
1346 pub(crate) fn lines(&self) -> LineIterator<'_> {
1347 LineIterator {
1348 sections: self.line_sections.iter(),
1349 blocks: DebugLinesBlockIterator::default(),
1350 lines: DebugLinesIterator::default(),
1351 columns: DebugColumnsIterator::default(),
1352 last_info: None,
1353 }
1354 }
1355
1356 pub(crate) fn lines_for_symbol(&self, offset: PdbInternalSectionOffset) -> LineIterator<'_> {
1357 let key = Self::lines_offset_key(offset);
1363 let index_result = self
1364 .line_sections
1365 .binary_search_by_key(&key, Self::lines_key);
1366
1367 let section = match index_result {
1368 Err(0) => return LineIterator::default(),
1369 Err(i) => self.line_sections[i - 1],
1370 Ok(i) => self.line_sections[i],
1371 };
1372
1373 if section.header.offset.section != offset.section {
1376 return LineIterator::default();
1377 }
1378
1379 LineIterator {
1380 sections: [].iter(),
1381 blocks: section.blocks(),
1382 lines: DebugLinesIterator::default(),
1383 columns: DebugColumnsIterator::default(),
1384 last_info: None,
1385 }
1386 }
1387
1388 pub(crate) fn files(&self) -> FileIterator<'a> {
1389 FileIterator {
1390 checksums: self.file_checksums.entries().unwrap_or_default(),
1391 }
1392 }
1393
1394 pub(crate) fn get_file_info(&self, index: FileIndex) -> Result<FileInfo<'a>> {
1395 let mut entries = self.file_checksums.entries_at_offset(index)?;
1398 let entry = entries
1399 .next()?
1400 .ok_or(Error::InvalidFileChecksumOffset(index.0))?;
1401
1402 Ok(FileInfo {
1403 name: entry.name,
1404 checksum: entry.checksum,
1405 })
1406 }
1407
1408 fn lines_offset_key(offset: PdbInternalSectionOffset) -> (u16, u32) {
1409 (offset.section, offset.offset)
1410 }
1411
1412 fn lines_key(lines: &DebugLinesSubsection<'_>) -> (u16, u32) {
1413 Self::lines_offset_key(lines.header.offset)
1414 }
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419 use super::*;
1420
1421 use std::mem;
1422
1423 use crate::symbol::BinaryAnnotations;
1424
1425 #[test]
1426 fn test_line_number_header() {
1427 assert_eq!(mem::size_of::<LineNumberHeader>(), 8);
1428 assert_eq!(mem::align_of::<LineNumberHeader>(), 4);
1429 }
1430
1431 #[test]
1432 fn test_column_number_header() {
1433 assert_eq!(mem::size_of::<ColumnNumberEntry>(), 4);
1434 assert_eq!(mem::align_of::<ColumnNumberEntry>(), 2);
1435 }
1436
1437 #[test]
1438 fn test_debug_lines_block_header() {
1439 assert_eq!(mem::size_of::<DebugLinesBlockHeader>(), 12);
1440 assert_eq!(mem::align_of::<DebugLinesBlockHeader>(), 4);
1441 }
1442
1443 #[test]
1444 fn test_raw_cross_scope_export() {
1445 assert_eq!(mem::size_of::<RawCrossScopeExport>(), 8);
1446 assert_eq!(mem::align_of::<RawCrossScopeExport>(), 4);
1447 }
1448
1449 #[test]
1450 fn test_iter_lines() {
1451 let data = &[
1452 244, 0, 0, 0, 24, 0, 0, 0, 169, 49, 0, 0, 16, 1, 115, 121, 2, 198, 45, 116, 88, 98,
1453 157, 13, 221, 82, 225, 34, 192, 51, 0, 0, 242, 0, 0, 0, 48, 0, 0, 0, 132, 160, 0, 0, 1,
1454 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 128,
1455 0, 0, 0, 0, 23, 0, 0, 128, 11, 0, 0, 0, 24, 0, 0, 128,
1456 ];
1457
1458 let line_program = LineProgram::parse(data).expect("parse line program");
1459 let lines: Vec<_> = line_program.lines().collect().expect("collect lines");
1460
1461 let expected = [
1462 LineInfo {
1463 offset: PdbInternalSectionOffset {
1464 section: 0x1,
1465 offset: 0xa084,
1466 },
1467 length: Some(0),
1468 file_index: FileIndex(0x0),
1469 line_start: 22,
1470 line_end: 22,
1471 column_start: None,
1472 column_end: None,
1473 kind: LineInfoKind::Statement,
1474 },
1475 LineInfo {
1476 offset: PdbInternalSectionOffset {
1477 section: 0x1,
1478 offset: 0xa084,
1479 },
1480 length: Some(11),
1481 file_index: FileIndex(0x0),
1482 line_start: 23,
1483 line_end: 23,
1484 column_start: None,
1485 column_end: None,
1486 kind: LineInfoKind::Statement,
1487 },
1488 LineInfo {
1489 offset: PdbInternalSectionOffset {
1490 section: 0x1,
1491 offset: 0xa08f,
1492 },
1493 length: Some(1),
1494 file_index: FileIndex(0x0),
1495 line_start: 24,
1496 line_end: 24,
1497 column_start: None,
1498 column_end: None,
1499 kind: LineInfoKind::Statement,
1500 },
1501 ];
1502
1503 assert_eq!(lines, expected);
1504 }
1505
1506 #[test]
1507 fn test_lines_for_symbol() {
1508 let data = &[
1509 244, 0, 0, 0, 24, 0, 0, 0, 169, 49, 0, 0, 16, 1, 115, 121, 2, 198, 45, 116, 88, 98,
1510 157, 13, 221, 82, 225, 34, 192, 51, 0, 0, 242, 0, 0, 0, 48, 0, 0, 0, 132, 160, 0, 0, 1,
1511 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 128,
1512 0, 0, 0, 0, 23, 0, 0, 128, 11, 0, 0, 0, 24, 0, 0, 128,
1513 ];
1514
1515 let offset = PdbInternalSectionOffset {
1516 section: 0x0001,
1517 offset: 0xa084,
1518 };
1519
1520 let line_program = LineProgram::parse(data).expect("parse line program");
1521 let line = line_program
1522 .lines_for_symbol(offset)
1523 .next()
1524 .expect("get line");
1525
1526 let expected = Some(LineInfo {
1527 offset: PdbInternalSectionOffset {
1528 section: 0x1,
1529 offset: 0xa084,
1530 },
1531 length: Some(0),
1532 file_index: FileIndex(0x0),
1533 line_start: 22,
1534 line_end: 22,
1535 column_start: None,
1536 column_end: None,
1537 kind: LineInfoKind::Statement,
1538 });
1539
1540 assert_eq!(expected, line);
1541 }
1542
1543 #[test]
1544 fn test_lines_for_symbol_asm() {
1545 let data = &[
1549 244, 0, 0, 0, 96, 0, 0, 0, 177, 44, 0, 0, 16, 1, 148, 43, 19, 100, 121, 95, 165, 113,
1550 45, 169, 112, 53, 233, 149, 174, 133, 0, 0, 248, 44, 0, 0, 16, 1, 54, 176, 28, 14, 163,
1551 149, 3, 189, 0, 215, 91, 24, 204, 45, 117, 241, 0, 0, 59, 45, 0, 0, 16, 1, 191, 40,
1552 129, 240, 15, 71, 114, 239, 184, 146, 206, 88, 119, 218, 136, 139, 0, 0, 126, 45, 0, 0,
1553 16, 1, 175, 252, 248, 34, 196, 152, 31, 107, 144, 61, 83, 41, 122, 95, 140, 123, 0, 0,
1554 242, 0, 0, 0, 96, 0, 0, 0, 112, 137, 0, 0, 1, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 9, 0,
1555 0, 0, 84, 0, 0, 0, 16, 0, 0, 0, 45, 0, 0, 128, 16, 0, 0, 0, 47, 0, 0, 128, 23, 0, 0, 0,
1556 48, 0, 0, 128, 26, 0, 0, 0, 49, 0, 0, 128, 30, 0, 0, 0, 50, 0, 0, 128, 35, 0, 0, 0, 51,
1557 0, 0, 128, 38, 0, 0, 0, 52, 0, 0, 128, 40, 0, 0, 0, 62, 0, 0, 128, 44, 0, 0, 0, 66, 0,
1558 0, 128,
1559 ];
1560
1561 let offset = PdbInternalSectionOffset {
1562 section: 0x0001,
1563 offset: 0x8990, };
1565
1566 let line_program = LineProgram::parse(data).expect("parse line program");
1567 let line = line_program
1568 .lines_for_symbol(offset)
1569 .next()
1570 .expect("get line");
1571
1572 let expected = Some(LineInfo {
1573 offset: PdbInternalSectionOffset {
1574 section: 0x1,
1575 offset: 0x8980,
1576 },
1577 length: Some(0),
1578 file_index: FileIndex(0x0),
1579 line_start: 45,
1580 line_end: 45,
1581 column_start: None,
1582 column_end: None,
1583 kind: LineInfoKind::Statement,
1584 });
1585
1586 assert_eq!(expected, line);
1587 }
1588
1589 #[test]
1590 fn test_parse_inlinee_lines() {
1591 let data = &[
1592 0, 0, 0, 0, 254, 18, 0, 0, 104, 1, 0, 0, 24, 0, 0, 0, 253, 18, 0, 0, 104, 1, 0, 0, 28,
1593 0, 0, 0,
1594 ];
1595
1596 let inlinee_lines = DebugInlineeLinesSubsection::parse(data).expect("parse inlinee lines");
1597 assert!(!inlinee_lines.header.has_extra_files());
1598
1599 let lines: Vec<_> = inlinee_lines
1600 .lines()
1601 .collect()
1602 .expect("collect inlinee lines");
1603
1604 let expected = [
1605 InlineeSourceLine {
1606 inlinee: IdIndex(0x12FE),
1607 file_id: FileIndex(0x168),
1608 line: 24,
1609 extra_files: &[],
1610 },
1611 InlineeSourceLine {
1612 inlinee: IdIndex(0x12FD),
1613 file_id: FileIndex(0x168),
1614 line: 28,
1615 extra_files: &[],
1616 },
1617 ];
1618
1619 assert_eq!(lines, expected);
1620 }
1621
1622 #[test]
1623 fn test_parse_inlinee_lines_with_files() {
1624 let data = &[
1625 1, 0, 0, 0, 235, 102, 9, 0, 232, 37, 0, 0, 19, 0, 0, 0, 1, 0, 0, 0, 216, 26, 0, 0, 240,
1626 163, 7, 0, 176, 44, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 120, 3, 0, 0,
1627 ];
1628
1629 let inlinee_lines = DebugInlineeLinesSubsection::parse(data).expect("parse inlinee lines");
1630 assert!(inlinee_lines.header.has_extra_files());
1631
1632 let lines: Vec<_> = inlinee_lines
1633 .lines()
1634 .collect()
1635 .expect("collect inlinee lines");
1636
1637 let expected = [
1638 InlineeSourceLine {
1639 inlinee: IdIndex(0x966EB),
1640 file_id: FileIndex(0x25e8),
1641 line: 19,
1642 extra_files: &[216, 26, 0, 0],
1643 },
1644 InlineeSourceLine {
1645 inlinee: IdIndex(0x7A3F0),
1646 file_id: FileIndex(0x2cb0),
1647 line: 120,
1648 extra_files: &[120, 3, 0, 0],
1649 },
1650 ];
1651
1652 assert_eq!(lines, expected)
1653 }
1654
1655 #[test]
1656 fn test_inlinee_lines() {
1657 let inline_site = InlineSiteSymbol {
1664 parent: Some(SymbolIndex(0x190)),
1665 end: SymbolIndex(0x1ec),
1666 inlinee: IdIndex(0x1180),
1667 invocations: None,
1668 annotations: BinaryAnnotations::new(&[12, 2, 63, 12, 3, 9, 0, 0]),
1669 };
1670
1671 let inlinee_line = InlineeSourceLine {
1673 inlinee: IdIndex(0x1180),
1674 file_id: FileIndex(0x270),
1675 line: 341,
1676 extra_files: &[],
1677 };
1678
1679 let parent_offset = PdbInternalSectionOffset {
1682 offset: 0x120,
1683 section: 0x1,
1684 };
1685
1686 let iter = InlineeLineIterator::new(parent_offset, &inline_site, inlinee_line);
1687 let lines: Vec<_> = iter.collect().expect("collect inlinee lines");
1688
1689 let expected = [
1690 LineInfo {
1691 offset: PdbInternalSectionOffset {
1692 section: 0x1,
1693 offset: 0x015f,
1694 },
1695 length: Some(2),
1696 file_index: FileIndex(0x270),
1697 line_start: 341,
1698 line_end: 342,
1699 column_start: None,
1700 column_end: None,
1701 kind: LineInfoKind::Statement,
1702 },
1703 LineInfo {
1704 offset: PdbInternalSectionOffset {
1705 section: 0x1,
1706 offset: 0x0168,
1707 },
1708 length: Some(3),
1709 file_index: FileIndex(0x270),
1710 line_start: 341,
1711 line_end: 342,
1712 column_start: None,
1713 column_end: None,
1714 kind: LineInfoKind::Statement,
1715 },
1716 ];
1717
1718 assert_eq!(lines, expected);
1719 }
1720
1721 #[test]
1722 fn test_inlinee_lines_length() {
1723 let inline_site = InlineSiteSymbol {
1731 parent: Some(SymbolIndex(0x03e5_14dc)),
1732 end: SymbolIndex(0x03e5_1bd0),
1733 inlinee: IdIndex(0xeb476),
1734 invocations: None,
1735 annotations: BinaryAnnotations::new(&[6, 38, 3, 186, 32, 11, 71, 11, 36, 4, 5, 0]),
1736 };
1737
1738 let inlinee_line = InlineeSourceLine {
1746 inlinee: IdIndex(0xeb476),
1747 file_id: FileIndex(0x590),
1748 line: 499,
1749 extra_files: &[],
1750 };
1751
1752 let parent_offset = PdbInternalSectionOffset {
1753 section: 0x1,
1754 offset: 0x0453_f100,
1755 };
1756
1757 let iter = InlineeLineIterator::new(parent_offset, &inline_site, inlinee_line);
1758 let lines: Vec<_> = iter.collect().expect("collect inlinee lines");
1759
1760 let expected = [
1761 LineInfo {
1762 offset: PdbInternalSectionOffset {
1763 section: 0x1,
1764 offset: 0x0454_2b20,
1765 },
1766 length: Some(7),
1767 file_index: FileIndex(0x590),
1768 line_start: 518,
1769 line_end: 519,
1770 column_start: None,
1771 column_end: None,
1772 kind: LineInfoKind::Statement,
1773 },
1774 LineInfo {
1775 offset: PdbInternalSectionOffset {
1776 section: 0x1,
1777 offset: 0x0454_2b27,
1778 },
1779 length: Some(4),
1780 file_index: FileIndex(0x590),
1781 line_start: 520,
1782 line_end: 521,
1783 column_start: None,
1784 column_end: None,
1785 kind: LineInfoKind::Statement,
1786 },
1787 LineInfo {
1788 offset: PdbInternalSectionOffset {
1789 section: 0x1,
1790 offset: 0x0454_2b2b,
1791 },
1792 length: Some(5),
1793 file_index: FileIndex(0x590),
1794 line_start: 521,
1795 line_end: 522,
1796 column_start: None,
1797 column_end: None,
1798 kind: LineInfoKind::Statement,
1799 },
1800 ];
1801
1802 assert_eq!(lines, expected);
1803 }
1804
1805 #[repr(align(4))]
1806 struct Align4<T>(T);
1807
1808 const CROSS_MODULE_IMPORT_DATA: Align4<[u8; 76]> = Align4([
1813 189, 44, 0, 0, 14, 0, 0, 0, 171, 19, 0, 128, 37, 20, 0, 128, 161, 19, 0, 128, 90, 20, 0, 128, 159, 19, 0, 128, 55, 20, 0, 128, 109, 17, 0, 128, 238, 17, 0, 128, 246, 19, 0, 128, 69, 20, 0, 128, 104, 19, 0, 128, 148, 20, 0, 128, 195, 20, 0, 128, 219, 20, 0, 128, 21, 222, 0, 0, 1, 0, 0, 0, 96, 22, 0, 128, ]);
1835
1836 #[test]
1837 fn test_parse_cross_section_imports() {
1838 let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1839
1840 let modules: Vec<_> = sec.modules().collect().expect("collect imports");
1841 assert_eq!(modules.len(), 2);
1842
1843 let module = modules[0];
1844 assert_eq!(module.get(0), Some(Local(IdIndex(0x8000_13AB))));
1845 assert_eq!(module.get(13), Some(Local(IdIndex(0x8000_14DB))));
1846 assert_eq!(module.get::<IdIndex>(14), None);
1847 }
1848
1849 #[test]
1850 fn test_resolve_cross_module_import() {
1851 let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1852
1853 let imports = CrossModuleImports::from_section(sec).expect("parse section");
1854 let cross_ref = imports
1855 .resolve_import(IdIndex(0x8000_000A))
1856 .expect("resolve import");
1857
1858 let expected = CrossModuleRef(
1859 ModuleRef(StringRef(0x2CBD)),
1861 Local(IdIndex(0x8000_1368)),
1863 );
1864
1865 assert_eq!(cross_ref, expected);
1866 }
1867
1868 #[test]
1869 fn test_resolve_cross_module_import2() {
1870 let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1871
1872 let imports = CrossModuleImports::from_section(sec).expect("parse section");
1873 let cross_ref = imports
1874 .resolve_import(IdIndex(0x8010_0000))
1875 .expect("resolve import");
1876
1877 let expected = CrossModuleRef(
1878 ModuleRef(StringRef(0xDE15)),
1880 Local(IdIndex(0x8000_1660)),
1882 );
1883
1884 assert_eq!(cross_ref, expected);
1885 }
1886
1887 const CROSS_MODULE_EXPORT_DATA: Align4<[u8; 32]> = Align4([
1888 31, 16, 0, 0, 12, 16, 0, 0, 32, 16, 0, 0, 79, 34, 0, 0, 92, 17, 0, 128, 97, 17, 0, 0, 109, 17, 0, 128, 98, 17, 0, 0, ]);
1893
1894 #[test]
1895 fn test_iter_cross_module_exports() {
1896 let section = DebugCrossScopeExportsSubsection::parse(&CROSS_MODULE_EXPORT_DATA.0)
1897 .expect("parse exports");
1898 let exports = CrossModuleExports::from_section(section).expect("parse section");
1899
1900 let exports: Vec<_> = exports.exports().collect().expect("collect exports");
1901
1902 let expected = [
1903 CrossModuleExport::Type(Local(TypeIndex(0x101F)), TypeIndex(0x100C)),
1904 CrossModuleExport::Type(Local(TypeIndex(0x1020)), TypeIndex(0x224F)),
1905 CrossModuleExport::Id(Local(IdIndex(0x8000_115C)), IdIndex(0x1161)),
1906 CrossModuleExport::Id(Local(IdIndex(0x8000_116D)), IdIndex(0x1162)),
1907 ];
1908
1909 assert_eq!(exports, expected);
1910 }
1911
1912 #[test]
1913 fn test_resolve_cross_module_ref() {
1914 let section = DebugCrossScopeExportsSubsection::parse(&CROSS_MODULE_EXPORT_DATA.0)
1915 .expect("parse exports");
1916 let exports = CrossModuleExports::from_section(section).expect("parse section");
1917
1918 let type_index = exports
1919 .resolve_import(Local(TypeIndex(0x101F)))
1920 .expect("resolve type");
1921 assert_eq!(type_index, Some(TypeIndex(0x100C)));
1922
1923 let id_index = exports
1924 .resolve_import(Local(IdIndex(0x8000_115C)))
1925 .expect("resolve id");
1926 assert_eq!(id_index, Some(IdIndex(0x1161)));
1927
1928 let missing_index = exports
1929 .resolve_import(Local(TypeIndex(0xFEED)))
1930 .expect("resolve missing");
1931 assert_eq!(missing_index, None);
1932 }
1933}