diff --git a/resym_core/src/pdb_file.rs b/resym_core/src/pdb_file.rs index 4544126..61df61c 100644 --- a/resym_core/src/pdb_file.rs +++ b/resym_core/src/pdb_file.rs @@ -6,7 +6,7 @@ use pdb::FallibleIterator; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::{ - collections::BTreeSet, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, io::{self, Read, Seek}, path::PathBuf, sync::{Arc, RwLock}, @@ -421,7 +421,7 @@ where let mut result = String::default(); module_info.symbols()?.for_each(|symbol| { - let mut needed_types = pdb_types::TypeSet::new(); + let mut needed_types = pdb_types::NeededTypeSet::new(); match symbol.parse()? { pdb::SymbolData::UserDefinedType(udt) => { @@ -515,55 +515,88 @@ where print_access_specifiers, }; let mut type_data = pdb_types::Data::new(); - let mut needed_types = pdb_types::TypeSet::new(); - // Add the requested type first - type_data.add( - type_finder, - &self.forwarder_to_complete_type, - type_index, - &primitives_flavor, - &mut needed_types, - )?; - - // If dependencies aren't needed, we're done + // If dependencies aren't needed, only process the given type index and return if !reconstruct_dependencies { + let mut needed_types = pdb_types::NeededTypeSet::new(); + type_data.add( + type_finder, + &self.forwarder_to_complete_type, + type_index, + &primitives_flavor, + &mut needed_types, + )?; + let mut reconstruction_output = String::new(); - type_data.reconstruct(&fmt_configuration, &mut reconstruction_output)?; + type_data.reconstruct( + &fmt_configuration, + &Default::default(), + &mut reconstruction_output, + )?; return Ok(reconstruction_output); } // Add all the needed types iteratively until we're done - let mut dependencies_data = pdb_types::Data::new(); - let mut processed_types = BTreeSet::from([type_index]); - let dep_start = Instant::now(); - loop { - // Get the first element in needed_types without holding an immutable borrow - let first = needed_types.difference(&processed_types).next().copied(); - match first { - None => break, - Some(needed_type_index) => { - // Add the type - dependencies_data.add( - type_finder, - &self.forwarder_to_complete_type, - needed_type_index, - &primitives_flavor, - &mut needed_types, - )?; + let mut type_dependency_map: HashMap> = + HashMap::new(); + { + let dep_start = Instant::now(); + + // Add the requested type first + let mut types_to_process: VecDeque = VecDeque::from([type_index]); + let mut processed_type_set = HashSet::from([]); + // Keep processing new types until there's nothing to process + while let Some(needed_type_index) = types_to_process.pop_front() { + if processed_type_set.contains(&needed_type_index) { + // Already processed, continue + continue; + } + + // Add the type + let mut needed_types = pdb_types::NeededTypeSet::new(); + type_data.add( + type_finder, + &self.forwarder_to_complete_type, + needed_type_index, + &primitives_flavor, + &mut needed_types, + )?; + + for pair in &needed_types { + // Add forward declaration for types referenced by pointers + if pair.1 { + type_data.add_as_forward_declaration(type_finder, pair.0)?; + } - processed_types.insert(needed_type_index); + // Update type dependency map + if let Some(type_dependency) = type_dependency_map.get_mut(&needed_type_index) { + type_dependency.push(*pair); + } else { + type_dependency_map.insert(needed_type_index, vec![*pair]); + } } + // Update the set of processed types + processed_type_set.insert(needed_type_index); + // Update the queue of type to process + types_to_process.extend(needed_types.into_iter().map(|pair| pair.0)); } + + log::debug!( + "Dependencies reconstruction took {} ms", + dep_start.elapsed().as_millis() + ); } - log::debug!( - "Dependencies reconstruction took {} ms", - dep_start.elapsed().as_millis() - ); + + // Deduce type "depth" from the dependency map + let type_depth_map = compute_type_depth_map(&type_dependency_map, &[type_index]); let mut reconstruction_output = String::new(); - dependencies_data.reconstruct(&fmt_configuration, &mut reconstruction_output)?; - type_data.reconstruct(&fmt_configuration, &mut reconstruction_output)?; + type_data.reconstruct( + &fmt_configuration, + &type_depth_map, + &mut reconstruction_output, + )?; + Ok(reconstruction_output) } @@ -585,7 +618,7 @@ where // Add the requested types type_iter = self.type_information.iter(); while let Some(item) = type_iter.next()? { - let mut needed_types = pdb_types::TypeSet::new(); + let mut needed_types = pdb_types::NeededTypeSet::new(); let type_index = item.index(); let result = type_data.add( &type_finder, @@ -607,11 +640,15 @@ where } } - let fmt_configuration = DataFormatConfiguration { - print_access_specifiers, - }; let mut reconstruction_output = String::new(); - type_data.reconstruct(&fmt_configuration, &mut reconstruction_output)?; + type_data.reconstruct( + &DataFormatConfiguration { + print_access_specifiers, + }, + &Default::default(), + &mut reconstruction_output, + )?; + Ok(reconstruction_output) } @@ -642,7 +679,7 @@ where let current_type_index = type_item.index(); // Reconstruct type and retrieve referenced types let mut type_data = pdb_types::Data::new(); - let mut needed_types = pdb_types::TypeSet::new(); + let mut needed_types = pdb_types::NeededTypeSet::new(); type_data.add( &type_finder, &self.forwarder_to_complete_type, @@ -651,7 +688,7 @@ where &mut needed_types, )?; - par_iter_if_available!(needed_types).for_each(|t| { + par_iter_if_available!(needed_types).for_each(|(t, _)| { if let Some(mut xref_list) = xref_map.get_mut(t) { xref_list.push(current_type_index); } else { @@ -695,3 +732,52 @@ where } } } + +fn compute_type_depth_map( + type_dependency_map: &HashMap>, + root_types: &[pdb::TypeIndex], +) -> BTreeMap> { + let depth_start = Instant::now(); + + let mut type_depth_map: HashMap = + HashMap::from_iter(root_types.iter().map(|elem| (*elem, 0))); + // Perform depth-first search to determine the "depth" of each type + let mut types_to_visit: VecDeque<(usize, pdb::TypeIndex)> = + VecDeque::from_iter(root_types.iter().map(|elem| (0, *elem))); + while let Some((current_type_depth, current_type_index)) = types_to_visit.pop_back() { + if let Some(type_dependencies) = type_dependency_map.get(¤t_type_index) { + for (child_type_index, child_is_pointer) in type_dependencies { + // Visit child only if it's directly referenced, to avoid infinite loops + if !child_is_pointer { + let current_child_depth = current_type_depth + 1; + if let Some(child_type_depth) = type_depth_map.get_mut(child_type_index) { + *child_type_depth = std::cmp::max(*child_type_depth, current_child_depth); + } else { + type_depth_map.insert(*child_type_index, current_child_depth); + } + types_to_visit.push_back((current_child_depth, *child_type_index)); + } + } + } + } + + // Invert type depth map + let inverted_type_depth_map: BTreeMap> = type_depth_map + .into_iter() + .fold(BTreeMap::new(), |mut acc, (type_index, type_depth)| { + if let Some(type_indices) = acc.get_mut(&type_depth) { + type_indices.push(type_index); + } else { + acc.insert(type_depth, vec![type_index]); + } + + acc + }); + + log::debug!( + "Depth calculation took {} ms", + depth_start.elapsed().as_millis() + ); + + inverted_type_depth_map +} diff --git a/resym_core/src/pdb_types/class.rs b/resym_core/src/pdb_types/class.rs index 9fb3e97..eaea025 100644 --- a/resym_core/src/pdb_types/class.rs +++ b/resym_core/src/pdb_types/class.rs @@ -7,7 +7,8 @@ use super::{ primitive_types::PrimitiveReconstructionFlavor, resolve_complete_type_index, type_bitfield_info, type_name, type_size, union::Union, - DataFormatConfiguration, Field, Method, Result, ResymCoreError, TypeForwarder, TypeSet, + DataFormatConfiguration, Field, Method, NeededTypeSet, ReconstructibleTypeData, Result, + ResymCoreError, TypeForwarder, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -52,6 +53,7 @@ pub struct BaseClass { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Class<'p> { + pub index: pdb::TypeIndex, pub kind: pdb::ClassKind, pub name: String, pub size: u64, @@ -71,7 +73,7 @@ impl<'p> Class<'p> { &mut self, _: &pdb::TypeFinder<'p>, _: pdb::TypeIndex, - _: &mut TypeSet, + _: &mut NeededTypeSet, ) -> Result<()> { // TODO Ok(()) @@ -83,7 +85,7 @@ impl<'p> Class<'p> { type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { // Resolve the complete type's index, if present in the PDB let complete_type_index = resolve_complete_type_index(type_forwarder, type_index); @@ -122,6 +124,7 @@ impl<'p> Class<'p> { }; let mut class = Class { + index: type_index, kind: data.kind, name, size: data.size, @@ -162,6 +165,7 @@ impl<'p> Class<'p> { }; let mut u = Union { + index: type_index, name, size: data.size, fields: Vec::new(), @@ -194,6 +198,7 @@ impl<'p> Class<'p> { }; let mut e = Enum { + index: type_index, name, underlying_type_name: type_name( type_finder, @@ -237,7 +242,7 @@ impl<'p> Class<'p> { type_forwarder: &TypeForwarder, field: &pdb::TypeData<'p>, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { match *field { pdb::TypeData::Member(ref data) => { @@ -410,8 +415,10 @@ impl<'p> Class<'p> { Ok(()) } +} - pub fn reconstruct( +impl ReconstructibleTypeData for Class<'_> { + fn reconstruct( &self, fmt_configuration: &DataFormatConfiguration, f: &mut impl std::fmt::Write, @@ -464,7 +471,7 @@ impl<'p> Class<'p> { if !self.nested_enums.is_empty() { writeln!(f, " ")?; for e in &self.nested_enums { - e.reconstruct(f)?; + e.reconstruct(fmt_configuration, f)?; } } diff --git a/resym_core/src/pdb_types/enumeration.rs b/resym_core/src/pdb_types/enumeration.rs index 9a89363..5b77b87 100644 --- a/resym_core/src/pdb_types/enumeration.rs +++ b/resym_core/src/pdb_types/enumeration.rs @@ -1,10 +1,11 @@ use std::fmt; -use super::TypeSet; +use super::{DataFormatConfiguration, NeededTypeSet, ReconstructibleTypeData}; use crate::error::Result; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Enum<'p> { + pub index: pdb::TypeIndex, pub name: String, pub underlying_type_name: String, pub values: Vec>, @@ -15,7 +16,7 @@ impl<'p> Enum<'p> { &mut self, type_finder: &pdb::TypeFinder<'p>, type_index: pdb::TypeIndex, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { match type_finder.find(type_index)?.parse()? { pdb::TypeData::FieldList(data) => { @@ -40,7 +41,12 @@ impl<'p> Enum<'p> { Ok(()) } - fn add_field(&mut self, _: &pdb::TypeFinder<'p>, field: &pdb::TypeData<'p>, _: &mut TypeSet) { + fn add_field( + &mut self, + _: &pdb::TypeFinder<'p>, + field: &pdb::TypeData<'p>, + _: &mut NeededTypeSet, + ) { // ignore everything else even though that's sad if let pdb::TypeData::Enumerate(ref data) = field { self.values.push(EnumValue { @@ -49,8 +55,14 @@ impl<'p> Enum<'p> { }); } } +} - pub fn reconstruct(&self, f: &mut impl std::fmt::Write) -> fmt::Result { +impl ReconstructibleTypeData for Enum<'_> { + fn reconstruct( + &self, + _fmt_configuration: &DataFormatConfiguration, + f: &mut impl std::fmt::Write, + ) -> fmt::Result { writeln!(f, "enum {} : {} {{", self.name, self.underlying_type_name)?; for value in &self.values { diff --git a/resym_core/src/pdb_types/forward_declaration.rs b/resym_core/src/pdb_types/forward_declaration.rs new file mode 100644 index 0000000..59d9bd7 --- /dev/null +++ b/resym_core/src/pdb_types/forward_declaration.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use super::{DataFormatConfiguration, ReconstructibleTypeData}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ForwardDeclaration { + pub index: pdb::TypeIndex, + pub kind: ForwardDeclarationKind, + pub name: String, +} + +impl ReconstructibleTypeData for ForwardDeclaration { + fn reconstruct( + &self, + _fmt_configuration: &DataFormatConfiguration, + f: &mut impl std::fmt::Write, + ) -> fmt::Result { + writeln!( + f, + "{} {};", + match self.kind { + ForwardDeclarationKind::Class => "class", + ForwardDeclarationKind::Struct => "struct", + ForwardDeclarationKind::Union => "union", + ForwardDeclarationKind::Interface => "interface", + }, + self.name + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ForwardDeclarationKind { + Class, + Struct, + Union, + Interface, +} + +impl ForwardDeclarationKind { + pub fn from_class_kind(class_kind: pdb::ClassKind) -> Self { + match class_kind { + pdb::ClassKind::Class => Self::Class, + pdb::ClassKind::Struct => Self::Struct, + pdb::ClassKind::Interface => Self::Interface, + } + } +} diff --git a/resym_core/src/pdb_types/forward_reference.rs b/resym_core/src/pdb_types/forward_reference.rs new file mode 100644 index 0000000..db87d53 --- /dev/null +++ b/resym_core/src/pdb_types/forward_reference.rs @@ -0,0 +1,29 @@ +use std::fmt; + +use super::{DataFormatConfiguration, ReconstructibleTypeData}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ForwardReference { + pub index: pdb::TypeIndex, + pub kind: pdb::ClassKind, + pub name: String, +} + +impl ReconstructibleTypeData for ForwardReference { + fn reconstruct( + &self, + _fmt_configuration: &DataFormatConfiguration, + f: &mut impl std::fmt::Write, + ) -> fmt::Result { + writeln!( + f, + "{} {};", + match self.kind { + pdb::ClassKind::Class => "class", + pdb::ClassKind::Struct => "struct", + pdb::ClassKind::Interface => "interface", // when can this happen? + }, + self.name + ) + } +} diff --git a/resym_core/src/pdb_types/method.rs b/resym_core/src/pdb_types/method.rs index aee6f16..722c3df 100644 --- a/resym_core/src/pdb_types/method.rs +++ b/resym_core/src/pdb_types/method.rs @@ -1,6 +1,6 @@ use super::{ argument_list, field::FieldAccess, primitive_types::PrimitiveReconstructionFlavor, type_name, - TypeForwarder, TypeSet, + NeededTypeSet, TypeForwarder, }; use crate::error::{Result, ResymCoreError}; @@ -26,7 +26,7 @@ impl<'p> Method<'p> { type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result> { match type_finder.find(type_index)?.parse()? { pdb::TypeData::MemberFunction(data) => Ok(Method { diff --git a/resym_core/src/pdb_types/mod.rs b/resym_core/src/pdb_types/mod.rs index d8a3bbd..35bdc9f 100644 --- a/resym_core/src/pdb_types/mod.rs +++ b/resym_core/src/pdb_types/mod.rs @@ -1,11 +1,13 @@ mod class; mod enumeration; mod field; +mod forward_declaration; +mod forward_reference; mod method; mod primitive_types; mod union; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, HashSet}; use std::fmt; use std::ops::Range; @@ -19,8 +21,14 @@ use union::Union; pub use primitive_types::{include_headers_for_flavor, PrimitiveReconstructionFlavor}; -/// Set of `TypeIndex` objets -pub type TypeSet = BTreeSet; +use self::forward_declaration::{ForwardDeclaration, ForwardDeclarationKind}; +use self::forward_reference::ForwardReference; + +/// Set of (`TypeIndex`, bool) tuples. +/// +/// The boolean value indicates whether the type was referenced via a pointer +/// or a C++ reference. +pub type NeededTypeSet = HashSet<(pdb::TypeIndex, bool)>; pub type TypeForwarder = dashmap::DashMap; @@ -30,7 +38,7 @@ pub fn type_name( type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<(String, String)> { let (type_left, type_right) = match type_finder.find(type_index)?.parse()? { pdb::TypeData::Primitive(data) => { @@ -41,7 +49,7 @@ pub fn type_name( } pdb::TypeData::Class(data) => { - needed_types.insert(type_index); + needed_types.insert((type_index, false)); // Rename unnamed anonymous tags to something unique let name = data.name.to_string(); if is_unnamed_type(&name) { @@ -53,7 +61,7 @@ pub fn type_name( } pdb::TypeData::Union(data) => { - needed_types.insert(type_index); + needed_types.insert((type_index, false)); // Rename unnamed anonymous tags to something unique let name = data.name.to_string(); if is_unnamed_type(&name) { @@ -65,7 +73,7 @@ pub fn type_name( } pdb::TypeData::Enumeration(data) => { - needed_types.insert(type_index); + needed_types.insert((type_index, false)); (data.name.to_string().into_owned(), String::default()) } @@ -73,13 +81,25 @@ pub fn type_name( // Resolve the complete type's index, if present in the PDB let complete_underlying_type_index = resolve_complete_type_index(type_forwarder, data.underlying_type); + let mut temporary_needed_types = HashSet::new(); let (type_left, type_right) = type_name( type_finder, type_forwarder, complete_underlying_type_index, primitive_flavor, - needed_types, + &mut temporary_needed_types, )?; + + if temporary_needed_types.len() < 2 { + // "Simple" type (e.g., class, union, enum) -> add as pointer + if let Some(needed_type) = temporary_needed_types.into_iter().next() { + needed_types.insert((needed_type.0, true)); + } + } else { + // "Complex" type (e.g., procedure) -> add as is + needed_types.extend(temporary_needed_types); + } + if data.attributes.is_reference() { (format!("{type_left}&"), type_right) } else { @@ -266,7 +286,7 @@ fn array_base_name( type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<((String, String), Vec)> { match type_finder.find(type_index)?.parse()? { pdb::TypeData::Array(data) => { @@ -323,7 +343,7 @@ pub fn argument_list( type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result> { match type_finder.find(type_index)?.parse()? { pdb::TypeData::ArgumentList(data) => { @@ -449,46 +469,90 @@ pub fn is_unnamed_type(type_name: &str) -> bool { || type_name.contains("__unnamed") } +/// Trait for type data that can be reconstructed to C++ +pub trait ReconstructibleTypeData { + fn reconstruct( + &self, + fmt_configuration: &DataFormatConfiguration, + f: &mut impl std::fmt::Write, + ) -> fmt::Result; +} + /// Struct that represent a set of reconstructed types (forward declarations, /// classes/structs, enums and unions) #[derive(Debug, Clone, PartialEq, Eq)] pub struct Data<'p> { - forward_references: Vec, - classes: Vec>, - enums: Vec>, - unions: Vec>, + /// Forward-declared types which are referenced in this PDB but not defined in it + forward_references: BTreeMap, + /// Forward-declared types which are defined in this PDB + forward_declarations: BTreeMap, + /// Enum types + enums: BTreeMap>, + /// Class/struct types + classes: BTreeMap>, + /// Union types + unions: BTreeMap>, } impl Data<'_> { pub fn reconstruct( &self, fmt_configuration: &DataFormatConfiguration, + type_depth_map: &BTreeMap>, f: &mut impl std::fmt::Write, - ) -> fmt::Result { + ) -> Result<()> { // Types without definition if !self.forward_references.is_empty() { writeln!(f)?; - for e in &self.forward_references { - e.reconstruct(f)?; - } + } + for e in self.forward_references.values() { + e.reconstruct(fmt_configuration, f)?; } - // Enum definitions - for e in &self.enums { + // Forward declarations + if !self.forward_declarations.is_empty() { writeln!(f)?; - e.reconstruct(f)?; + } + for e in self.forward_declarations.values() { + e.reconstruct(fmt_configuration, f)?; } - // Class/struct definitions - for class in &self.classes { + // Enum definitions + for e in self.enums.values() { writeln!(f)?; - class.reconstruct(fmt_configuration, f)?; + e.reconstruct(fmt_configuration, f)?; } - // Union definitions - for u in &self.unions { - writeln!(f)?; - u.reconstruct(fmt_configuration, f)?; + if !type_depth_map.is_empty() { + // Follow type depth map order + for type_indices in type_depth_map.values().rev() { + for type_index in type_indices.iter() { + // Class definitions + if let Some(class) = self.classes.get(type_index) { + writeln!(f)?; + class.reconstruct(fmt_configuration, f)?; + } + // Union definitions + else if let Some(union) = self.unions.get(type_index) { + writeln!(f)?; + union.reconstruct(fmt_configuration, f)?; + } + } + } + } else { + // Follow type index order + // + // Class/struct definitions + for class in self.classes.values() { + writeln!(f)?; + class.reconstruct(fmt_configuration, f)?; + } + + // Union definitions + for u in self.unions.values() { + writeln!(f)?; + u.reconstruct(fmt_configuration, f)?; + } } Ok(()) @@ -504,10 +568,11 @@ impl<'p> Default for Data<'p> { impl<'p> Data<'p> { pub fn new() -> Self { Self { - forward_references: Vec::new(), - classes: Vec::new(), - enums: Vec::new(), - unions: Vec::new(), + forward_references: BTreeMap::new(), + forward_declarations: BTreeMap::new(), + classes: BTreeMap::new(), + enums: BTreeMap::new(), + unions: BTreeMap::new(), } } @@ -517,7 +582,7 @@ impl<'p> Data<'p> { type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { match type_finder.find(type_index)?.parse()? { pdb::TypeData::Class(data) => { @@ -530,17 +595,22 @@ impl<'p> Data<'p> { }; if data.properties.forward_reference() { - self.forward_references.push(ForwardReference { - kind: data.kind, - name, - }); + self.forward_references.insert( + type_index, + ForwardReference { + index: type_index, + kind: data.kind, + name, + }, + ); return Ok(()); } let mut class = Class { + index: type_index, kind: data.kind, - name, + name: name.clone(), size: data.size, fields: Vec::new(), static_fields: Vec::new(), @@ -576,7 +646,7 @@ impl<'p> Data<'p> { } } - self.classes.insert(0, class); + self.classes.insert(type_index, class); } pdb::TypeData::Union(data) => { @@ -589,7 +659,8 @@ impl<'p> Data<'p> { }; let mut u = Union { - name, + index: type_index, + name: name.clone(), size: data.size, fields: Vec::new(), static_fields: Vec::new(), @@ -614,7 +685,7 @@ impl<'p> Data<'p> { ); } - self.unions.insert(0, u); + self.unions.insert(type_index, u); } pdb::TypeData::Enumeration(data) => { @@ -627,7 +698,8 @@ impl<'p> Data<'p> { }; let mut e = Enum { - name, + index: type_index, + name: name.clone(), underlying_type_name: type_name( type_finder, type_forwarder, @@ -647,7 +719,7 @@ impl<'p> Data<'p> { ); } - self.enums.insert(0, e); + self.enums.insert(type_index, e); } // ignore @@ -656,6 +728,56 @@ impl<'p> Data<'p> { Ok(()) } + + pub fn add_as_forward_declaration( + &mut self, + type_finder: &pdb::TypeFinder<'p>, + type_index: pdb::TypeIndex, + ) -> Result<()> { + match type_finder.find(type_index)?.parse()? { + pdb::TypeData::Class(data) => { + let name_str = data.name.to_string(); + // Rename unnamed anonymous tags to something unique + let name = if is_unnamed_type(&name_str) { + format!("_unnamed_{type_index}") + } else { + name_str.into_owned() + }; + + self.forward_declarations.insert( + type_index, + ForwardDeclaration { + index: type_index, + kind: ForwardDeclarationKind::from_class_kind(data.kind), + name, + }, + ); + } + + pdb::TypeData::Union(data) => { + let name_str = data.name.to_string(); + // Rename unnamed anonymous tags to something unique + let name = if is_unnamed_type(&name_str) { + format!("_unnamed_{type_index}") + } else { + name_str.into_owned() + }; + + self.forward_declarations.insert( + type_index, + ForwardDeclaration { + index: type_index, + kind: ForwardDeclarationKind::Union, + name, + }, + ); + } + + _ => {} + } + + Ok(()) + } } pub fn resolve_complete_type_index( @@ -959,27 +1081,6 @@ fn find_unnamed_structs_in_unions(fields: &[Field]) -> Vec> { structs_found } -#[derive(Debug, Clone, PartialEq, Eq)] -struct ForwardReference { - kind: pdb::ClassKind, - name: String, -} - -impl ForwardReference { - pub fn reconstruct(&self, f: &mut impl std::fmt::Write) -> fmt::Result { - writeln!( - f, - "{} {};", - match self.kind { - pdb::ClassKind::Class => "class", - pdb::ClassKind::Struct => "struct", - pdb::ClassKind::Interface => "interface", // when can this happen? - }, - self.name - ) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct DataFormatConfiguration { pub print_access_specifiers: bool, diff --git a/resym_core/src/pdb_types/union.rs b/resym_core/src/pdb_types/union.rs index e7eef2c..dd91d28 100644 --- a/resym_core/src/pdb_types/union.rs +++ b/resym_core/src/pdb_types/union.rs @@ -7,12 +7,13 @@ use super::{ fmt_union_fields_recursive, is_unnamed_type, primitive_types::PrimitiveReconstructionFlavor, resolve_complete_type_index, type_bitfield_info, type_name, type_size, DataFormatConfiguration, - Field, Method, TypeForwarder, TypeSet, + Field, Method, NeededTypeSet, ReconstructibleTypeData, TypeForwarder, }; use crate::error::{Result, ResymCoreError}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Union<'p> { + pub index: pdb::TypeIndex, pub name: String, pub size: u64, pub fields: Vec>, @@ -31,7 +32,7 @@ impl<'p> Union<'p> { type_forwarder: &TypeForwarder, type_index: pdb::TypeIndex, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { // Resolve the complete type's index, if present in the PDB let complete_type_index = resolve_complete_type_index(type_forwarder, type_index); @@ -70,6 +71,7 @@ impl<'p> Union<'p> { }; let mut class = Class { + index: type_index, kind: data.kind, name, size: data.size, @@ -110,6 +112,7 @@ impl<'p> Union<'p> { }; let mut u = Union { + index: type_index, name, size: data.size, fields: Vec::new(), @@ -142,6 +145,7 @@ impl<'p> Union<'p> { }; let mut e = Enum { + index: type_index, name, underlying_type_name: type_name( type_finder, @@ -181,7 +185,7 @@ impl<'p> Union<'p> { type_forwarder: &TypeForwarder, field: &pdb::TypeData<'p>, primitive_flavor: &PrimitiveReconstructionFlavor, - needed_types: &mut TypeSet, + needed_types: &mut NeededTypeSet, ) -> Result<()> { match *field { pdb::TypeData::Member(ref data) => { @@ -313,8 +317,10 @@ impl<'p> Union<'p> { Ok(()) } +} - pub fn reconstruct( +impl ReconstructibleTypeData for Union<'_> { + fn reconstruct( &self, fmt_configuration: &DataFormatConfiguration, f: &mut impl std::fmt::Write, @@ -337,7 +343,7 @@ impl<'p> Union<'p> { if !self.nested_enums.is_empty() { writeln!(f, " ")?; for e in &self.nested_enums { - e.reconstruct(f)?; + e.reconstruct(fmt_configuration, f)?; } }