• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ergrelet / resym / 8350875382

19 Mar 2024 10:21PM UTC coverage: 40.158% (+0.07%) from 40.092%
8350875382

push

github

ergrelet
WIP: compilable output

90 of 177 new or added lines in 6 files covered. (50.85%)

2 existing lines in 2 files now uncovered.

1267 of 3155 relevant lines covered (40.16%)

1.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.24
/resym_core/src/pdb_file.rs
1
use dashmap::DashMap;
2
#[cfg(target_arch = "wasm32")]
3
use instant::Instant;
4
use pdb::FallibleIterator;
5
#[cfg(feature = "rayon")]
6
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
7

8
use std::{
9
    collections::{BTreeMap, HashMap, HashSet, VecDeque},
10
    io::{self, Read, Seek},
11
    path::PathBuf,
12
    sync::{Arc, RwLock},
13
};
14
#[cfg(not(target_arch = "wasm32"))]
15
use std::{fs::File, path::Path, time::Instant};
16

17
use crate::{
18
    error::{Result, ResymCoreError},
19
    find_any_if_available,
20
    frontend::ModuleList,
21
    par_iter_if_available,
22
    pdb_types::{
23
        self, is_unnamed_type, type_name, DataFormatConfiguration, PrimitiveReconstructionFlavor,
24
    },
25
};
26

27
/// Wrapper for different buffer types processed by `resym`
28
#[derive(Debug)]
29
pub enum PDBDataSource {
30
    File(std::fs::File),
31
    Vec(io::Cursor<Vec<u8>>),
32
    SharedArray(io::Cursor<Arc<[u8]>>),
33
}
34

35
impl Seek for PDBDataSource {
36
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
6✔
37
        match self {
6✔
38
            PDBDataSource::File(file) => file.seek(pos),
6✔
39
            PDBDataSource::Vec(vec) => vec.seek(pos),
×
40
            PDBDataSource::SharedArray(array) => array.seek(pos),
×
41
        }
42
    }
43
}
44

45
impl Read for PDBDataSource {
46
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
6✔
47
        match self {
6✔
48
            PDBDataSource::File(file) => file.read(buf),
6✔
49
            PDBDataSource::Vec(vec) => vec.read(buf),
×
50
            PDBDataSource::SharedArray(array) => array.read(buf),
×
51
        }
52
    }
53
}
54

55
pub struct PdbFile<'p, T>
56
where
57
    T: io::Seek + io::Read + 'p,
58
{
59
    pub complete_type_list: Vec<(String, pdb::TypeIndex)>,
60
    pub forwarder_to_complete_type: Arc<DashMap<pdb::TypeIndex, pdb::TypeIndex>>,
61
    pub machine_type: pdb::MachineType,
62
    pub type_information: pdb::TypeInformation<'p>,
63
    pub debug_information: pdb::DebugInformation<'p>,
64
    pub file_path: PathBuf,
65
    pub xref_map: RwLock<DashMap<pdb::TypeIndex, Vec<pdb::TypeIndex>>>,
66
    pdb: RwLock<pdb::PDB<'p, T>>,
67
}
68

69
#[cfg(not(target_arch = "wasm32"))]
70
impl<'p> PdbFile<'p, File> {
71
    /// Create `PdbFile` from an `std::path::Path`
72
    pub fn load_from_file(pdb_file_path: &Path) -> Result<PdbFile<'p, PDBDataSource>> {
6✔
73
        let file = PDBDataSource::File(File::open(pdb_file_path)?);
7✔
74
        let mut pdb = pdb::PDB::open(file)?;
12✔
75
        let type_information = pdb.type_information()?;
12✔
76
        let debug_information = pdb.debug_information()?;
12✔
77
        let machine_type = pdb.debug_information()?.machine_type()?;
12✔
78

79
        let mut pdb_file = PdbFile {
80
            complete_type_list: vec![],
6✔
81
            forwarder_to_complete_type: Arc::new(DashMap::default()),
12✔
82
            machine_type,
83
            type_information,
84
            debug_information,
85
            file_path: pdb_file_path.to_owned(),
6✔
86
            xref_map: DashMap::default().into(),
12✔
87
            pdb: pdb.into(),
6✔
88
        };
89
        pdb_file.load_symbols()?;
18✔
90

91
        Ok(pdb_file)
6✔
92
    }
93
}
94

95
impl<'p> PdbFile<'p, PDBDataSource> {
96
    /// Create `PdbFile` from a `String` and a `Vec<u8>`
97
    pub fn load_from_bytes_as_vec(
×
98
        pdb_file_name: String,
99
        pdb_file_data: Vec<u8>,
100
    ) -> Result<PdbFile<'p, PDBDataSource>> {
101
        let reader = PDBDataSource::Vec(io::Cursor::new(pdb_file_data));
×
102
        let mut pdb = pdb::PDB::open(reader)?;
×
103
        let type_information = pdb.type_information()?;
×
104
        let debug_information = pdb.debug_information()?;
×
105
        let machine_type = pdb.debug_information()?.machine_type()?;
×
106

107
        let mut pdb_file = PdbFile {
108
            complete_type_list: vec![],
×
109
            forwarder_to_complete_type: Arc::new(DashMap::default()),
×
110
            machine_type,
111
            type_information,
112
            debug_information,
113
            file_path: pdb_file_name.into(),
×
114
            xref_map: DashMap::default().into(),
×
115
            pdb: pdb.into(),
×
116
        };
117
        pdb_file.load_symbols()?;
×
118

119
        Ok(pdb_file)
×
120
    }
121

122
    /// Create `PdbFile` from a `String` and a `Arc<[u8]>`
123
    pub fn load_from_bytes_as_array(
×
124
        pdb_file_name: String,
125
        pdb_file_data: Arc<[u8]>,
126
    ) -> Result<PdbFile<'p, PDBDataSource>> {
127
        let reader = PDBDataSource::SharedArray(io::Cursor::new(pdb_file_data));
×
128
        let mut pdb = pdb::PDB::open(reader)?;
×
129
        let type_information = pdb.type_information()?;
×
130
        let debug_information = pdb.debug_information()?;
×
131
        let machine_type = pdb.debug_information()?.machine_type()?;
×
132

133
        let mut pdb_file = PdbFile {
134
            complete_type_list: vec![],
×
135
            forwarder_to_complete_type: Arc::new(DashMap::default()),
×
136
            machine_type,
137
            type_information,
138
            debug_information,
139
            file_path: pdb_file_name.into(),
×
140
            xref_map: DashMap::default().into(),
×
141
            pdb: pdb.into(),
×
142
        };
143
        pdb_file.load_symbols()?;
×
144

145
        Ok(pdb_file)
×
146
    }
147
}
148

149
impl<'p, T> PdbFile<'p, T>
150
where
151
    T: io::Seek + io::Read + std::fmt::Debug + 'p,
152
{
153
    fn load_symbols(&mut self) -> Result<()> {
6✔
154
        // Build the list of complete types
155
        let complete_symbol_map: DashMap<String, pdb::TypeIndex> = DashMap::default();
6✔
156
        let mut forwarders = vec![];
6✔
157
        let pdb_start = Instant::now();
12✔
158

159
        let mut type_finder = self.type_information.finder();
6✔
160
        let mut type_info_iter = self.type_information.iter();
12✔
161
        while let Some(type_info) = type_info_iter.next()? {
12✔
162
            // keep building the index
163
            type_finder.update(&type_info_iter);
6✔
164

165
            let type_index = type_info.index();
6✔
166
            if let Ok(type_data) = type_info.parse() {
6✔
167
                match type_data {
6✔
168
                    pdb::TypeData::Class(data) => {
6✔
169
                        let mut class_name = data.name.to_string().into_owned();
12✔
170

171
                        // Ignore forward references
172
                        if data.properties.forward_reference() {
12✔
173
                            forwarders.push((class_name, type_index));
6✔
174
                            continue;
×
175
                        }
176
                        complete_symbol_map.insert(class_name.clone(), type_index);
12✔
177

178
                        // Rename anonymous tags to something unique
179
                        if is_unnamed_type(&class_name) {
12✔
180
                            class_name = format!("_unnamed_{type_index}");
6✔
181
                        }
182
                        self.complete_type_list.push((class_name, type_index));
6✔
183
                    }
184
                    pdb::TypeData::Union(data) => {
6✔
185
                        let mut class_name = data.name.to_string().into_owned();
12✔
186

187
                        // Ignore forward references
188
                        if data.properties.forward_reference() {
12✔
189
                            forwarders.push((class_name, type_index));
6✔
190
                            continue;
×
191
                        }
192
                        complete_symbol_map.insert(class_name.clone(), type_index);
12✔
193

194
                        // Rename anonymous tags to something unique
195
                        if is_unnamed_type(&class_name) {
12✔
196
                            class_name = format!("_unnamed_{type_index}");
6✔
197
                        }
198
                        self.complete_type_list.push((class_name, type_index));
6✔
199
                    }
200
                    pdb::TypeData::Enumeration(data) => {
6✔
201
                        let mut class_name = data.name.to_string().into_owned();
12✔
202

203
                        // Ignore forward references
204
                        if data.properties.forward_reference() {
12✔
205
                            forwarders.push((class_name, type_index));
4✔
206
                            continue;
×
207
                        }
208
                        complete_symbol_map.insert(class_name.clone(), type_index);
12✔
209

210
                        // Rename anonymous tags to something unique
211
                        if is_unnamed_type(&class_name) {
12✔
212
                            class_name = format!("_unnamed_{type_index}");
6✔
213
                        }
214
                        self.complete_type_list.push((class_name, type_index));
6✔
215
                    }
216
                    _ => {}
×
217
                }
218
            }
219
        }
220
        log::debug!("PDB loading took {} ms", pdb_start.elapsed().as_millis());
18✔
221

222
        // Resolve forwarder references to their corresponding complete type, in parallel
223
        let fwd_start = Instant::now();
12✔
224
        par_iter_if_available!(forwarders).for_each(|(fwd_name, fwd_type_id)| {
18✔
225
            if let Some(complete_type_index) = complete_symbol_map.get(fwd_name) {
6✔
226
                self.forwarder_to_complete_type
12✔
227
                    .insert(*fwd_type_id, *complete_type_index);
6✔
228
            } else {
229
                log::debug!("'{}''s type definition wasn't found", fwd_name);
18✔
230
            }
231
        });
232
        log::debug!(
12✔
233
            "Forwarder resolution took {} ms",
234
            fwd_start.elapsed().as_millis()
×
235
        );
236

237
        Ok(())
6✔
238
    }
239

240
    pub fn reconstruct_type_by_name(
3✔
241
        &self,
242
        type_name: &str,
243
        primitives_flavor: PrimitiveReconstructionFlavor,
244
        reconstruct_dependencies: bool,
245
        print_access_specifiers: bool,
246
    ) -> Result<String> {
247
        // Populate our `TypeFinder` and find the right type index
248
        let mut type_index = pdb::TypeIndex::default();
3✔
249
        let mut type_finder = self.type_information.finder();
3✔
250
        {
251
            let mut type_iter = self.type_information.iter();
6✔
252
            while let Some(item) = type_iter.next()? {
6✔
253
                type_finder.update(&type_iter);
3✔
254

255
                let item_type_index = item.index();
3✔
256
                if let Ok(type_data) = item.parse() {
3✔
257
                    match type_data {
3✔
258
                        pdb::TypeData::Class(data) => {
3✔
259
                            if data.properties.forward_reference() {
6✔
260
                                // Ignore incomplete type
261
                                continue;
×
262
                            }
263

264
                            // Rename anonymous tags to something unique
265
                            let class_name = data.name.to_string();
3✔
266
                            if is_unnamed_type(&class_name) {
6✔
267
                                if type_name == format!("_unnamed_{item_type_index}") {
3✔
268
                                    type_index = item_type_index;
×
269
                                }
270
                            } else if class_name == type_name {
9✔
271
                                type_index = item_type_index;
3✔
272
                            } else if let Some(unique_name) = data.unique_name {
6✔
273
                                if unique_name.to_string() == type_name {
6✔
274
                                    type_index = item_type_index;
×
275
                                }
276
                            }
277
                        }
278
                        pdb::TypeData::Union(data) => {
3✔
279
                            if data.properties.forward_reference() {
6✔
280
                                // Ignore incomplete type
281
                                continue;
×
282
                            }
283

284
                            // Rename anonymous tags to something unique
285
                            let union_name = data.name.to_string();
3✔
286
                            if is_unnamed_type(&union_name) {
6✔
287
                                if type_name == format!("_unnamed_{item_type_index}") {
3✔
288
                                    type_index = item_type_index;
×
289
                                }
290
                            } else if data.name.to_string() == type_name {
7✔
291
                                type_index = item_type_index;
1✔
292
                            } else if let Some(unique_name) = data.unique_name {
3✔
293
                                if unique_name.to_string() == type_name {
6✔
294
                                    type_index = item_type_index;
×
295
                                }
296
                            }
297
                        }
298
                        pdb::TypeData::Enumeration(data) => {
3✔
299
                            if data.properties.forward_reference() {
6✔
300
                                // Ignore incomplete type
301
                                continue;
×
302
                            }
303

304
                            // Rename anonymous tags to something unique
305
                            let enum_name = data.name.to_string();
3✔
306
                            if is_unnamed_type(&enum_name) {
6✔
307
                                if type_name == format!("_unnamed_{item_type_index}") {
3✔
308
                                    type_index = item_type_index;
×
309
                                }
310
                            } else if data.name.to_string() == type_name {
7✔
311
                                type_index = item_type_index;
1✔
312
                            } else if let Some(unique_name) = data.unique_name {
3✔
313
                                if unique_name.to_string() == type_name {
6✔
314
                                    type_index = item_type_index;
×
315
                                }
316
                            }
317
                        }
318
                        // Ignore
319
                        _ => {}
×
320
                    }
321
                }
322
            }
323
        }
324

325
        if type_index == pdb::TypeIndex::default() {
7✔
326
            Err(ResymCoreError::TypeNameNotFoundError(type_name.to_owned()))
2✔
327
        } else {
328
            self.reconstruct_type_by_type_index_internal(
6✔
329
                &type_finder,
×
330
                type_index,
3✔
331
                primitives_flavor,
×
332
                reconstruct_dependencies,
×
333
                print_access_specifiers,
×
334
            )
335
        }
336
    }
337

338
    pub fn reconstruct_type_by_type_index(
×
339
        &self,
340
        type_index: pdb::TypeIndex,
341
        primitives_flavor: PrimitiveReconstructionFlavor,
342
        reconstruct_dependencies: bool,
343
        print_access_specifiers: bool,
344
    ) -> Result<String> {
345
        // Populate our `TypeFinder`
346
        let mut type_finder = self.type_information.finder();
×
347
        {
348
            let mut type_iter = self.type_information.iter();
×
349
            while (type_iter.next()?).is_some() {
×
350
                type_finder.update(&type_iter);
×
351
            }
352
        }
353

354
        self.reconstruct_type_by_type_index_internal(
×
355
            &type_finder,
×
356
            type_index,
×
357
            primitives_flavor,
×
358
            reconstruct_dependencies,
×
359
            print_access_specifiers,
×
360
        )
361
    }
362

363
    pub fn module_list(&self) -> Result<ModuleList> {
2✔
364
        let module_list = self
2✔
365
            .debug_information
×
366
            .modules()?
367
            .enumerate()
368
            .map(|(index, module)| Ok((module.module_name().into_owned(), index)));
4✔
369

370
        Ok(module_list.collect()?)
4✔
371
    }
372

373
    pub fn reconstruct_module_by_path(
3✔
374
        &self,
375
        module_path: &str,
376
        primitives_flavor: PrimitiveReconstructionFlavor,
377
    ) -> Result<String> {
378
        // Find index for module
379
        let mut modules = self.debug_information.modules()?;
3✔
380
        let module_index = modules.position(|module| Ok(module.module_name() == module_path))?;
12✔
381

382
        match module_index {
3✔
383
            None => Err(ResymCoreError::ModuleNotFoundError(format!(
×
384
                "Module '{}' not found",
×
385
                module_path
×
386
            ))),
387
            Some(module_index) => self.reconstruct_module_by_index(module_index, primitives_flavor),
3✔
388
        }
389
    }
390

391
    pub fn reconstruct_module_by_index(
3✔
392
        &self,
393
        module_index: usize,
394
        primitives_flavor: PrimitiveReconstructionFlavor,
395
    ) -> Result<String> {
396
        let mut modules = self.debug_information.modules()?;
3✔
397
        let module = modules.nth(module_index)?.ok_or_else(|| {
6✔
398
            ResymCoreError::ModuleInfoNotFoundError(format!("Module #{} not found", module_index))
×
399
        })?;
400

401
        let module_info = self
9✔
402
            .pdb
×
403
            .write()
404
            .expect("lock shouldn't be poisoned")
405
            .module_info(&module)?
×
406
            .ok_or_else(|| {
3✔
407
                ResymCoreError::ModuleInfoNotFoundError(format!(
×
408
                    "No module information present for '{}'",
×
409
                    module.object_file_name()
×
410
                ))
411
            })?;
412

413
        // Populate our `TypeFinder`
414
        let mut type_finder = self.type_information.finder();
3✔
415
        {
416
            let mut type_iter = self.type_information.iter();
6✔
417
            while (type_iter.next()?).is_some() {
3✔
418
                type_finder.update(&type_iter);
6✔
419
            }
420
        }
421

422
        let mut result = String::default();
3✔
423
        module_info.symbols()?.for_each(|symbol| {
12✔
424
            let mut needed_types = pdb_types::NeededTypeSet::new();
3✔
425

426
            match symbol.parse()? {
6✔
427
                pdb::SymbolData::UserDefinedType(udt) => {
3✔
428
                    if let Ok(type_name) = type_name(
6✔
429
                        &type_finder,
3✔
430
                        &self.forwarder_to_complete_type,
3✔
431
                        udt.type_index,
3✔
432
                        &primitives_flavor,
3✔
433
                        &mut needed_types,
×
434
                    ) {
435
                        if type_name.0 == "..." {
6✔
436
                            // No type
437
                            result +=
×
438
                                format!("{}; // Missing type information\n", udt.name).as_str();
×
439
                        } else {
440
                            result +=
6✔
441
                                format!("using {} = {}{};\n", udt.name, type_name.0, type_name.1)
6✔
442
                                    .as_str();
3✔
443
                        }
444
                    }
445
                }
446
                pdb::SymbolData::Procedure(procedure) => {
3✔
447
                    if let Ok(type_name) = type_name(
6✔
448
                        &type_finder,
3✔
449
                        &self.forwarder_to_complete_type,
3✔
450
                        procedure.type_index,
3✔
451
                        &primitives_flavor,
3✔
452
                        &mut needed_types,
×
453
                    ) {
454
                        if type_name.0 == "..." {
6✔
455
                            // No type
456
                            result += format!(
8✔
457
                                "void {}(); // CodeSize={} (missing type information)\n",
458
                                procedure.name, procedure.len
×
459
                            )
460
                            .as_str();
2✔
461
                        } else {
462
                            result += format!(
18✔
463
                                "{}{}{}; // CodeSize={}\n",
464
                                type_name.0, procedure.name, type_name.1, procedure.len
×
465
                            )
466
                            .as_str();
3✔
467
                        }
468
                    }
469
                }
470
                pdb::SymbolData::Data(data) => {
3✔
471
                    if let Ok(type_name) = type_name(
6✔
472
                        &type_finder,
3✔
473
                        &self.forwarder_to_complete_type,
3✔
474
                        data.type_index,
3✔
475
                        &primitives_flavor,
3✔
476
                        &mut needed_types,
×
477
                    ) {
478
                        if type_name.0 == "..." {
6✔
479
                            // No type
480
                            result +=
×
481
                                format!("{}; // Missing type information\n", data.name).as_str();
×
482
                        } else {
483
                            result +=
6✔
484
                                format!("{} {}{};\n", type_name.0, data.name, type_name.1).as_str();
9✔
485
                        }
486
                    }
487
                }
488
                pdb::SymbolData::UsingNamespace(namespace) => {
3✔
489
                    result += format!("using namespace {};\n", namespace.name).as_str();
6✔
490
                }
491
                pdb::SymbolData::AnnotationReference(annotation) => {
×
492
                    // TODO(ergrelet): update when support for annotations
493
                    // (symbol kind 0x1019) has been implemented in `pdb`
494
                    result += format!("__annotation(); // {}\n", annotation.name).as_str();
×
495
                }
496
                // Ignore
497
                _ => {}
×
498
            }
499

500
            Ok(())
3✔
501
        })?;
502

503
        Ok(result)
3✔
504
    }
505

506
    fn reconstruct_type_by_type_index_internal(
3✔
507
        &self,
508
        type_finder: &pdb::TypeFinder,
509
        type_index: pdb::TypeIndex,
510
        primitives_flavor: PrimitiveReconstructionFlavor,
511
        reconstruct_dependencies: bool,
512
        print_access_specifiers: bool,
513
    ) -> Result<String> {
514
        let fmt_configuration = DataFormatConfiguration {
515
            print_access_specifiers,
516
        };
517
        let mut type_data = pdb_types::Data::new();
3✔
518

519
        // If dependencies aren't needed, only process the given type index and return
520
        if !reconstruct_dependencies {
3✔
521
            let mut needed_types = pdb_types::NeededTypeSet::new();
3✔
522
            type_data.add(
3✔
NEW
523
                type_finder,
×
524
                &self.forwarder_to_complete_type,
3✔
NEW
525
                type_index,
×
NEW
526
                &primitives_flavor,
×
NEW
527
                &mut needed_types,
×
528
            )?;
529

530
            let mut reconstruction_output = String::new();
3✔
531
            type_data.reconstruct(
9✔
NEW
532
                &fmt_configuration,
×
533
                &Default::default(),
3✔
NEW
534
                &mut reconstruction_output,
×
535
            )?;
536
            return Ok(reconstruction_output);
3✔
537
        }
538

539
        // Add all the needed types iteratively until we're done
540
        let mut type_dependency_map: HashMap<pdb::TypeIndex, Vec<(pdb::TypeIndex, bool)>> =
1✔
NEW
541
            HashMap::new();
×
542
        {
543
            let dep_start = Instant::now();
2✔
544

545
            // Add the requested type first
546
            let mut types_to_process: VecDeque<pdb::TypeIndex> = VecDeque::from([type_index]);
1✔
547
            let mut processed_type_set = HashSet::from([]);
2✔
548
            // Keep processing new types until there's nothing to process
549
            while let Some(needed_type_index) = types_to_process.pop_front() {
3✔
550
                if processed_type_set.contains(&needed_type_index) {
2✔
551
                    // Already processed, continue
NEW
552
                    continue;
×
553
                }
554

555
                // Add the type
556
                let mut needed_types = pdb_types::NeededTypeSet::new();
1✔
557
                type_data.add(
2✔
NEW
558
                    type_finder,
×
559
                    &self.forwarder_to_complete_type,
1✔
560
                    needed_type_index,
1✔
NEW
561
                    &primitives_flavor,
×
NEW
562
                    &mut needed_types,
×
563
                )?;
564

565
                // Update type dependency map
566
                needed_types.iter().for_each(|pair| {
2✔
NEW
567
                    if let Some(type_dependency) = type_dependency_map.get_mut(&needed_type_index) {
×
NEW
568
                        type_dependency.push(*pair);
×
569
                    } else {
NEW
570
                        type_dependency_map.insert(needed_type_index, vec![*pair]);
×
571
                    }
572
                });
573
                // Update the set of processed types
574
                processed_type_set.insert(needed_type_index);
1✔
575
                // Update the queue of type to process
576
                types_to_process.extend(needed_types.into_iter().map(|pair| pair.0));
1✔
577
            }
578

579
            log::debug!(
3✔
580
                "Dependencies reconstruction took {} ms",
NEW
581
                dep_start.elapsed().as_millis()
×
582
            );
583
        }
584

585
        // Deduce type "depth" from the dependency map
586
        let mut type_depth_map: HashMap<pdb::TypeIndex, usize> = HashMap::from([(type_index, 0)]);
1✔
587
        {
588
            // Perform depth-first search to determine the "depth" of each type
589
            let mut types_to_visit: VecDeque<(usize, pdb::TypeIndex)> =
1✔
NEW
590
                VecDeque::from([(0, type_index)]);
×
591
            let mut visit_stack: VecDeque<(usize, pdb::TypeIndex)> = VecDeque::new();
2✔
592
            while let Some((current_type_depth, current_type_index)) = types_to_visit.pop_back() {
2✔
593
                // Update type visit stack
594
                while let Some((type_depth, _)) = visit_stack.back() {
2✔
NEW
595
                    if *type_depth >= current_type_depth {
×
NEW
596
                        visit_stack.pop_back();
×
597
                    } else {
NEW
598
                        break;
×
599
                    }
600
                }
601
                visit_stack.push_back((current_type_depth, current_type_index));
1✔
602

603
                if let Some(type_dependencies) = type_dependency_map.get(&current_type_index) {
1✔
NEW
604
                    for (child_type_index, child_is_pointer) in type_dependencies {
×
605
                        // Visit child only if it's not already on the stack, to avoid loops
606
                        // if !visit_stack.iter().any(|(_, t)| t == child_type_index) {
NEW
607
                        if !child_is_pointer {
×
NEW
608
                            let current_child_depth = current_type_depth + 1;
×
NEW
609
                            if let Some(child_type_depth) = type_depth_map.get_mut(child_type_index)
×
610
                            {
NEW
611
                                *child_type_depth =
×
NEW
612
                                    std::cmp::max(*child_type_depth, current_child_depth);
×
613
                            } else {
NEW
614
                                type_depth_map.insert(*child_type_index, current_child_depth);
×
615
                            }
NEW
616
                            types_to_visit.push_back((current_child_depth, *child_type_index));
×
617
                        } else {
618
                            // Add a forward reference as it's most likely a pointer
NEW
619
                            type_data.add_as_forward_reference(type_finder, *child_type_index)?;
×
620
                        }
621
                    }
622
                }
623
            }
624
        }
625

626
        // Invert type depth map
627
        let inverted_type_depth_map: BTreeMap<usize, Vec<pdb::TypeIndex>> = type_depth_map
2✔
628
            .into_iter()
629
            .fold(BTreeMap::new(), |mut acc, (type_index, type_depth)| {
3✔
630
                if let Some(type_indices) = acc.get_mut(&type_depth) {
2✔
NEW
631
                    type_indices.push(type_index);
×
632
                } else {
633
                    acc.insert(type_depth, vec![type_index]);
2✔
634
                }
635

636
                acc
1✔
637
            });
638

639
        let mut reconstruction_output = String::new();
1✔
640
        type_data.reconstruct(
5✔
NEW
641
            &fmt_configuration,
×
NEW
642
            &inverted_type_depth_map,
×
NEW
643
            &mut reconstruction_output,
×
644
        )?;
645
        Ok(reconstruction_output)
1✔
646
    }
647

648
    pub fn reconstruct_all_types(
1✔
649
        &self,
650
        primitives_flavor: PrimitiveReconstructionFlavor,
651
        print_access_specifiers: bool,
652
    ) -> Result<String> {
653
        let mut type_data = pdb_types::Data::new();
1✔
654

655
        let mut type_finder = self.type_information.finder();
1✔
656
        {
657
            // Populate our `TypeFinder`
658
            let mut type_iter = self.type_information.iter();
2✔
659
            while (type_iter.next()?).is_some() {
1✔
660
                type_finder.update(&type_iter);
2✔
661
            }
662

663
            // Add the requested types
664
            type_iter = self.type_information.iter();
2✔
665
            while let Some(item) = type_iter.next()? {
1✔
666
                let mut needed_types = pdb_types::NeededTypeSet::new();
1✔
667
                let type_index = item.index();
2✔
668
                let result = type_data.add(
1✔
669
                    &type_finder,
×
670
                    &self.forwarder_to_complete_type,
1✔
671
                    type_index,
1✔
672
                    &primitives_flavor,
×
673
                    &mut needed_types,
×
674
                );
675
                if let Err(err) = result {
1✔
676
                    match err {
1✔
677
                        ResymCoreError::PdbError(err) => {
1✔
678
                            // Ignore this kind of error since some particular PDB features might not be supported.
679
                            // This allows the recontruction to go through with the correctly reconstructed types.
680
                            log::warn!("Failed to reconstruct type with index {type_index}: {err}")
3✔
681
                        }
682
                        _ => return Err(err),
×
683
                    }
684
                }
685
            }
686
        }
687

688
        let fmt_configuration = DataFormatConfiguration {
689
            print_access_specifiers,
690
        };
691
        let mut reconstruction_output = String::new();
1✔
692
        type_data.reconstruct(
3✔
NEW
693
            &fmt_configuration,
×
694
            &Default::default(),
1✔
NEW
695
            &mut reconstruction_output,
×
696
        )?;
697
        Ok(reconstruction_output)
1✔
698
    }
699

700
    pub fn get_xrefs_for_type(
×
701
        &self,
702
        type_index: pdb::TypeIndex,
703
    ) -> Result<Vec<(String, pdb::TypeIndex)>> {
704
        // Generate xref cache if empty
705
        if self
×
706
            .xref_map
×
707
            .read()
708
            .expect("lock shouldn't be poisoned")
709
            .is_empty()
710
        {
711
            // Populate our `TypeFinder`
712
            let mut type_finder = self.type_information.finder();
×
713
            {
714
                let mut type_iter = self.type_information.iter();
×
715
                while (type_iter.next()?).is_some() {
×
716
                    type_finder.update(&type_iter);
×
717
                }
718
            }
719

720
            // Iterate through all types
721
            let xref_map: DashMap<pdb::TypeIndex, Vec<pdb::TypeIndex>> = DashMap::default();
×
722
            let mut type_iter = self.type_information.iter();
×
723
            while let Some(type_item) = type_iter.next()? {
×
724
                let current_type_index = type_item.index();
×
725
                // Reconstruct type and retrieve referenced types
726
                let mut type_data = pdb_types::Data::new();
×
NEW
727
                let mut needed_types = pdb_types::NeededTypeSet::new();
×
728
                type_data.add(
×
729
                    &type_finder,
×
730
                    &self.forwarder_to_complete_type,
×
731
                    current_type_index,
×
732
                    &PrimitiveReconstructionFlavor::Raw,
×
733
                    &mut needed_types,
×
734
                )?;
735

NEW
736
                par_iter_if_available!(needed_types).for_each(|(t, _)| {
×
737
                    if let Some(mut xref_list) = xref_map.get_mut(t) {
×
738
                        xref_list.push(current_type_index);
×
739
                    } else {
740
                        xref_map.insert(*t, vec![current_type_index]);
×
741
                    }
742
                });
743
            }
744

745
            // Update cache
746
            if let Ok(mut xref_map_ref) = self.xref_map.write() {
×
747
                *xref_map_ref = xref_map;
×
748
            }
749
        }
750

751
        // Query xref cache
752
        if let Some(xref_list) = self
×
753
            .xref_map
×
754
            .read()
755
            .expect("lock shouldn't be poisoned")
756
            .get(&type_index)
×
757
        {
758
            // Convert the xref list into a proper Name+TypeIndex tuple list
759
            let xref_type_list = xref_list
×
760
                .iter()
761
                .map(|xref_type_index| {
×
762
                    // Look for the corresponding tuple (in parallel if possible)
763
                    let tuple = find_any_if_available!(
×
764
                        self.complete_type_list,
×
765
                        |(_, type_index)| type_index == xref_type_index
×
766
                    )
767
                    .expect("`complete_type_list` should contain type index");
×
768

769
                    tuple.clone()
×
770
                })
771
                .collect();
772

773
            Ok(xref_type_list)
×
774
        } else {
775
            // No xrefs found for the given type
776
            Ok(vec![])
×
777
        }
778
    }
779
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc