diff --git a/resym/src/resym_app.rs b/resym/src/resym_app.rs index 2bb99ca..2dd06d9 100644 --- a/resym/src/resym_app.rs +++ b/resym/src/resym_app.rs @@ -3,7 +3,8 @@ use eframe::egui; use memory_logger::blocking::MemoryLogger; use resym_core::{ backend::{Backend, BackendCommand, PDBSlot}, - frontend::{FrontendCommand, TypeIndex}, + frontend::FrontendCommand, + pdb_file::{SymbolIndex, TypeIndex}, }; #[cfg(target_arch = "wasm32")] @@ -18,8 +19,8 @@ use crate::{ module_tree::{ModuleInfo, ModulePath}, settings::ResymAppSettings, ui_components::{ - CodeViewComponent, ConsoleComponent, ModuleTreeComponent, SettingsComponent, - TextSearchComponent, TypeListComponent, TypeListOrdering, + CodeViewComponent, ConsoleComponent, IndexListComponent, IndexListOrdering, + ModuleTreeComponent, SettingsComponent, TextSearchComponent, }, }; @@ -44,6 +45,7 @@ impl From for PDBSlot { #[derive(PartialEq)] enum LeftPanelTab { TypeSearch, + SymbolSearch, ModuleBrowsing, } @@ -62,18 +64,21 @@ pub struct ResymApp { // Components used in the left-side panel left_panel_selected_tab: LeftPanelTab, type_search: TextSearchComponent, - type_list: TypeListComponent, + type_list: IndexListComponent, + selected_type_index: Option, + symbol_search: TextSearchComponent, + symbol_list: IndexListComponent, + selected_symbol_index: Option, module_search: TextSearchComponent, module_tree: ModuleTreeComponent, code_view: CodeViewComponent, // Components used in the bottom panel bottom_panel_selected_tab: BottomPanelTab, console: ConsoleComponent, - xref_to_list: TypeListComponent, - xref_from_list: TypeListComponent, + xref_to_list: IndexListComponent, + xref_from_list: IndexListComponent, // Other components settings: SettingsComponent, - selected_type_index: Option, #[cfg(feature = "http")] open_url: OpenURLComponent, frontend_controller: Arc, @@ -159,15 +164,18 @@ impl ResymApp { current_mode: ResymAppMode::Idle, left_panel_selected_tab: LeftPanelTab::TypeSearch, type_search: TextSearchComponent::new(), - type_list: TypeListComponent::new(TypeListOrdering::Alphabetical), + type_list: IndexListComponent::new(IndexListOrdering::Alphabetical), + selected_type_index: None, + symbol_search: TextSearchComponent::new(), + symbol_list: IndexListComponent::new(IndexListOrdering::Alphabetical), + selected_symbol_index: None, module_search: TextSearchComponent::new(), module_tree: ModuleTreeComponent::new(), code_view: CodeViewComponent::new(), bottom_panel_selected_tab: BottomPanelTab::Console, console: ConsoleComponent::new(logger), - xref_to_list: TypeListComponent::new(TypeListOrdering::Alphabetical), - xref_from_list: TypeListComponent::new(TypeListOrdering::Alphabetical), - selected_type_index: None, + xref_to_list: IndexListComponent::new(IndexListOrdering::Alphabetical), + xref_from_list: IndexListComponent::new(IndexListOrdering::Alphabetical), settings: SettingsComponent::new(app_settings), #[cfg(feature = "http")] open_url: OpenURLComponent::new(), @@ -209,6 +217,11 @@ impl ResymApp { LeftPanelTab::TypeSearch, "Search types", ); + ui.selectable_value( + &mut self.left_panel_selected_tab, + LeftPanelTab::SymbolSearch, + "Search symbols", + ); ui.selectable_value( &mut self.left_panel_selected_tab, LeftPanelTab::ModuleBrowsing, @@ -296,6 +309,82 @@ impl ResymApp { // Update the type list self.type_list.update(ui, &mut on_type_selected); } + + LeftPanelTab::SymbolSearch => { + // Callback run when the search query changes + let on_query_update = |search_query: &str| { + // Update filtered list if filter has changed + let result = if let ResymAppMode::Comparing(..) = self.current_mode { + self.backend.send_command(BackendCommand::ListSymbolsMerged( + vec![ + ResymPDBSlots::Main as usize, + ResymPDBSlots::Diff as usize, + ], + search_query.to_string(), + self.settings.app_settings.search_case_insensitive, + self.settings.app_settings.search_use_regex, + self.settings.app_settings.ignore_std_types, + )) + } else { + self.backend.send_command(BackendCommand::ListSymbols( + ResymPDBSlots::Main as usize, + search_query.to_string(), + self.settings.app_settings.search_case_insensitive, + self.settings.app_settings.search_use_regex, + self.settings.app_settings.ignore_std_types, + )) + }; + if let Err(err) = result { + log::error!("Failed to update type filter value: {}", err); + } + }; + + // Update the symbol search bar + ui.label("Search"); + self.symbol_search.update(ui, &on_query_update); + ui.separator(); + ui.add_space(4.0); + + // Callback run when a type is selected in the list + let mut on_symbol_selected = + |symbol_name: &str, symbol_index: SymbolIndex| { + // Update currently selected type index + self.selected_symbol_index = Some(symbol_index); + + match self.current_mode { + ResymAppMode::Browsing(..) => { + if let Err(err) = self.backend.send_command( + BackendCommand::ReconstructSymbolByIndex( + ResymPDBSlots::Main as usize, + symbol_index, + self.settings.app_settings.primitive_types_flavor, + self.settings.app_settings.print_header, + ), + ) { + log::error!("Failed to reconstruct type: {}", err); + } + } + ResymAppMode::Comparing(..) => { + if let Err(err) = self.backend.send_command( + BackendCommand::DiffSymbolByName( + ResymPDBSlots::Main as usize, + ResymPDBSlots::Diff as usize, + symbol_name.to_string(), + self.settings.app_settings.primitive_types_flavor, + self.settings.app_settings.print_header, + ), + ) { + log::error!("Failed to reconstruct type diff: {}", err); + } + } + _ => log::error!("Invalid application state"), + } + }; + + // Update the symbol list + self.symbol_list.update(ui, &mut on_symbol_selected); + } + LeftPanelTab::ModuleBrowsing => { // Callback run when the search query changes let on_query_update = |search_query: &str| match self.current_mode { @@ -557,8 +646,8 @@ impl ResymApp { // Reset selected type self.selected_type_index = None; // Reset xref lists - self.xref_to_list.update_type_list(vec![]); - self.xref_from_list.update_type_list(vec![]); + self.xref_to_list.update_index_list(vec![]); + self.xref_from_list.update_index_list(vec![]); // Request a type list update if let Err(err) = self.backend.send_command(BackendCommand::ListTypes( @@ -570,6 +659,18 @@ impl ResymApp { )) { log::error!("Failed to update type filter value: {}", err); } + // Request a symbol list update + if let Err(err) = + self.backend.send_command(BackendCommand::ListSymbols( + ResymPDBSlots::Main as usize, + String::default(), + false, + false, + self.settings.app_settings.ignore_std_types, + )) + { + log::error!("Failed to update type filter value: {}", err); + } // Request a module list update if let Err(err) = self.backend.send_command(BackendCommand::ListModules( @@ -593,8 +694,8 @@ impl ResymApp { // Reset selected type self.selected_type_index = None; // Reset xref lists - self.xref_to_list.update_type_list(vec![]); - self.xref_from_list.update_type_list(vec![]); + self.xref_to_list.update_index_list(vec![]); + self.xref_from_list.update_index_list(vec![]); // Request a type list update if let Err(err) = @@ -653,22 +754,50 @@ impl ResymApp { ); // Update xref lists - self.xref_to_list.update_type_list(vec![]); - self.xref_from_list.update_type_list(xrefs_from); + self.xref_to_list.update_index_list(vec![]); + self.xref_from_list.update_index_list(xrefs_from); // Switch to the "xref from" tab self.bottom_panel_selected_tab = BottomPanelTab::XRefsFrom; } } } - FrontendCommand::UpdateModuleList(module_list_result) => match module_list_result { - Err(err) => { - log::error!("Failed to retrieve module list: {}", err); + FrontendCommand::ListModulesResult(module_list_result) => { + match module_list_result { + Err(err) => { + log::error!("Failed to retrieve module list: {}", err); + } + Ok(module_list) => { + self.module_tree.set_module_list(module_list); + } } - Ok(module_list) => { - self.module_tree.set_module_list(module_list); + } + + FrontendCommand::ReconstructSymbolResult(result) => { + match result { + Err(err) => { + let error_msg = format!("Failed to reconstruct symbol: {}", err); + log::error!("{}", &error_msg); + + // Show an empty "reconstruted" view + self.current_mode = + ResymAppMode::Browsing(Default::default(), 0, error_msg); + } + Ok(reconstructed_symbol) => { + let last_line_number = 1 + reconstructed_symbol.lines().count(); + let line_numbers = + (1..last_line_number).fold(String::default(), |mut acc, e| { + let _r = writeln!(&mut acc, "{e}"); + acc + }); + self.current_mode = ResymAppMode::Browsing( + line_numbers, + last_line_number, + reconstructed_symbol, + ); + } } - }, + } FrontendCommand::ReconstructModuleResult(module_reconstruction_result) => { match module_reconstruction_result { @@ -752,7 +881,12 @@ impl ResymApp { FrontendCommand::ListTypesResult(filtered_types) => { // Update type list component - self.type_list.update_type_list(filtered_types); + self.type_list.update_index_list(filtered_types); + } + + FrontendCommand::ListSymbolsResult(filtered_symbols) => { + // Update symbol list component + self.symbol_list.update_index_list(filtered_symbols); } FrontendCommand::ListTypeCrossReferencesResult(xref_list_result) => { @@ -765,7 +899,7 @@ impl ResymApp { log::info!("{xref_count} cross-references found!"); // Update xref list component - self.xref_to_list.update_type_list(xref_list); + self.xref_to_list.update_index_list(xref_list); // Switch to xref tab self.bottom_panel_selected_tab = BottomPanelTab::XRefsTo; } @@ -883,7 +1017,7 @@ impl ResymApp { fn list_xrefs_for_type(&self, type_index: TypeIndex) { log::info!( "Looking for cross-references for type #0x{:x}...", - type_index.0 + type_index ); if let Err(err) = self .backend @@ -894,7 +1028,7 @@ impl ResymApp { { log::error!( "Failed to list cross-references to type #0x{:x}: {err}", - type_index.0 + type_index ); } } diff --git a/resym/src/ui_components/type_list.rs b/resym/src/ui_components/index_list.rs similarity index 64% rename from resym/src/ui_components/type_list.rs rename to resym/src/ui_components/index_list.rs index a8346c0..0eb0d05 100644 --- a/resym/src/ui_components/type_list.rs +++ b/resym/src/ui_components/index_list.rs @@ -1,45 +1,40 @@ use eframe::egui::{self, ScrollArea, TextStyle}; -use resym_core::frontend::{TypeIndex, TypeList}; -pub struct TypeListComponent { - filtered_type_list: TypeList, +pub struct IndexListComponent { + index_list: Vec<(String, I)>, selected_row: usize, - list_ordering: TypeListOrdering, + list_ordering: IndexListOrdering, } -pub enum TypeListOrdering { +pub enum IndexListOrdering { /// Doesn't respect any particular order None, /// Orders types alphabetically Alphabetical, } -impl TypeListComponent { - pub fn new(ordering: TypeListOrdering) -> Self { +impl IndexListComponent { + pub fn new(ordering: IndexListOrdering) -> Self { Self { - filtered_type_list: vec![], + index_list: vec![], selected_row: usize::MAX, list_ordering: ordering, } } - pub fn update_type_list(&mut self, type_list: TypeList) { - self.filtered_type_list = type_list; + pub fn update_index_list(&mut self, index_list: Vec<(String, I)>) { + self.index_list = index_list; self.selected_row = usize::MAX; // Reorder list if needed - if let TypeListOrdering::Alphabetical = self.list_ordering { - self.filtered_type_list + if let IndexListOrdering::Alphabetical = self.list_ordering { + self.index_list .sort_unstable_by(|lhs, rhs| lhs.0.cmp(&rhs.0)); } } - pub fn update( - &mut self, - ui: &mut egui::Ui, - on_type_selected: &mut CB, - ) { - let num_rows = self.filtered_type_list.len(); + pub fn update(&mut self, ui: &mut egui::Ui, on_element_selected: &mut CB) { + let num_rows = self.index_list.len(); const TEXT_STYLE: TextStyle = TextStyle::Body; let row_height = ui.text_style_height(&TEXT_STYLE); ui.with_layout( @@ -55,14 +50,14 @@ impl TypeListComponent { .auto_shrink([false, false]) .show_rows(ui, row_height, num_rows, |ui, row_range| { for row_index in row_range { - let (type_name, type_index) = &self.filtered_type_list[row_index]; + let (type_name, type_index) = &self.index_list[row_index]; if ui .selectable_label(self.selected_row == row_index, type_name) .clicked() { self.selected_row = row_index; - on_type_selected(type_name, *type_index); + on_element_selected(type_name, *type_index); } } }); @@ -71,8 +66,8 @@ impl TypeListComponent { } } -impl Default for TypeListComponent { +impl Default for IndexListComponent { fn default() -> Self { - Self::new(TypeListOrdering::None) + Self::new(IndexListOrdering::None) } } diff --git a/resym/src/ui_components/mod.rs b/resym/src/ui_components/mod.rs index 3f10b59..2874d76 100644 --- a/resym/src/ui_components/mod.rs +++ b/resym/src/ui_components/mod.rs @@ -1,17 +1,17 @@ mod code_view; mod console; +mod index_list; mod module_tree; #[cfg(feature = "http")] mod open_url; mod settings; mod text_search; -mod type_list; pub use code_view::*; pub use console::*; +pub use index_list::*; pub use module_tree::*; #[cfg(feature = "http")] pub use open_url::*; pub use settings::*; pub use text_search::*; -pub use type_list::*; diff --git a/resym/src/ui_components/module_tree.rs b/resym/src/ui_components/module_tree.rs index 7149dc1..17c5520 100644 --- a/resym/src/ui_components/module_tree.rs +++ b/resym/src/ui_components/module_tree.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use eframe::egui::{self, ScrollArea}; -use resym_core::frontend::ModuleList; +use resym_core::pdb_file::ModuleList; use crate::{ module_tree::{ModuleInfo, ModulePath, ModuleTreeNode}, diff --git a/resym_core/src/backend.rs b/resym_core/src/backend.rs index 12df8ee..688fbed 100644 --- a/resym_core/src/backend.rs +++ b/resym_core/src/backend.rs @@ -4,10 +4,11 @@ use instant::Instant; #[cfg(feature = "rayon")] use rayon::{ iter::{IntoParallelRefIterator, ParallelIterator}, - slice::ParallelSliceMut, + prelude::ParallelSliceMut, ThreadPool, }; +use core::fmt; #[cfg(all(not(feature = "rayon"), not(target_arch = "wasm32")))] use std::thread::{self, JoinHandle}; use std::{ @@ -20,14 +21,12 @@ use std::{path::PathBuf, time::Instant}; #[cfg(all(not(feature = "rayon"), target_arch = "wasm32"))] use wasm_thread::{self as thread, JoinHandle}; -use crate::{diffing::diff_module_by_path, frontend::ReconstructedType, pdb_file::PDBDataSource}; use crate::{ - diffing::diff_type_by_name, + diffing::{diff_module_by_path, diff_type_by_name}, error::{Result, ResymCoreError}, - frontend::FrontendCommand, - frontend::{FrontendController, ModuleList}, + frontend::{FrontendCommand, FrontendController, ReconstructedType}, par_iter_if_available, par_sort_by_if_available, - pdb_file::PdbFile, + pdb_file::{self, ModuleList, PDBDataSource, PdbFile, SymbolList, TypeList}, pdb_types::{include_headers_for_flavor, PrimitiveReconstructionFlavor}, PKG_VERSION, }; @@ -50,7 +49,7 @@ pub enum BackendCommand { /// Reconstruct a type given its type index for a given PDB. ReconstructTypeByIndex( PDBSlot, - pdb::TypeIndex, + pdb_file::TypeIndex, PrimitiveReconstructionFlavor, bool, bool, @@ -74,7 +73,21 @@ pub enum BackendCommand { /// Retrieve a list of types that match the given filter for multiple PDBs /// and merge the result. ListTypesMerged(Vec, String, bool, bool, bool), - /// Retrieve the list of all modules in a given PDB. + /// Retrieve a list of symbols that match the given filter for multiple PDBs + /// and merge the result. + ListSymbols(PDBSlot, String, bool, bool, bool), + /// Retrieve a list of symbols that match the given filter for multiple PDBs + /// and merge the result. + ListSymbolsMerged(Vec, String, bool, bool, bool), + /// Reconstruct a symbol given its index for a given PDB. + ReconstructSymbolByIndex( + PDBSlot, + pdb_file::SymbolIndex, + PrimitiveReconstructionFlavor, + bool, + ), + /// Retrieve a list of modules that match the given filter for multiple PDBs + /// and merge the result. ListModules(PDBSlot, String, bool, bool), /// Reconstruct a module given its index for a given PDB. ReconstructModuleByIndex(PDBSlot, usize, PrimitiveReconstructionFlavor, bool), @@ -89,6 +102,14 @@ pub enum BackendCommand { bool, bool, ), + /// Reconstruct the diff of a symbol given its name. + DiffSymbolByName( + PDBSlot, + PDBSlot, + String, + PrimitiveReconstructionFlavor, + bool, + ), /// Reconstruct the diff of a module given its path. DiffModuleByPath( PDBSlot, @@ -98,7 +119,7 @@ pub enum BackendCommand { bool, ), /// Retrieve a list of all types that reference the given type - ListTypeCrossReferences(PDBSlot, pdb::TypeIndex), + ListTypeCrossReferences(PDBSlot, pdb_file::TypeIndex), } /// Struct that represents the backend. The backend is responsible @@ -391,7 +412,7 @@ fn worker_thread_routine( // Collapse all type indices to `default`. When merging // type lists, we can only count on type names to // represent the types. - (s, pdb::TypeIndex::default()) + (s, Default::default()) })); } } @@ -400,6 +421,86 @@ fn worker_thread_routine( ))?; } + BackendCommand::ListSymbols( + pdb_slot, + search_filter, + case_insensitive_search, + use_regex, + ignore_std_types, + ) => { + if let Some(pdb_file) = pdb_files.get(&pdb_slot) { + let filtered_symbol_list = update_symbol_filter_command( + pdb_file, + &search_filter, + case_insensitive_search, + use_regex, + ignore_std_types, + ); + frontend_controller + .send_command(FrontendCommand::ListSymbolsResult(filtered_symbol_list))?; + } + } + + BackendCommand::ListSymbolsMerged( + pdb_slots, + search_filter, + case_insensitive_search, + use_regex, + ignore_std_types, + ) => { + let mut filtered_symbol_set = BTreeSet::default(); + for pdb_slot in pdb_slots { + if let Some(pdb_file) = pdb_files.get(&pdb_slot) { + let filtered_symbol_list = update_symbol_filter_command( + pdb_file, + &search_filter, + case_insensitive_search, + use_regex, + ignore_std_types, + ); + filtered_symbol_set.extend(filtered_symbol_list.into_iter().map( + |(s, _)| { + // Collapse all type indices to `default`. When merging + // type lists, we can only count on type names to + // represent the types. + (s, Default::default()) + }, + )); + } + } + frontend_controller.send_command(FrontendCommand::ListSymbolsResult( + filtered_symbol_set.into_iter().collect(), + ))?; + } + + BackendCommand::ReconstructSymbolByIndex( + pdb_slot, + symbol_index, + primitives_flavor, + print_header, + ) => { + if let Some(pdb_file) = pdb_files.get_mut(&pdb_slot) { + let result = reconstruct_symbol_by_index_command( + pdb_file, + symbol_index, + primitives_flavor, + print_header, + ); + frontend_controller + .send_command(FrontendCommand::ReconstructSymbolResult(result))?; + } + } + + BackendCommand::DiffSymbolByName( + pdb_from_slot, + pdb_to_slot, + symbol_name, + primitives_flavor, + print_header, + ) => { + todo!(); + } + BackendCommand::ReconstructModuleByIndex( pdb_slot, module_index, @@ -434,7 +535,7 @@ fn worker_thread_routine( use_regex, ); frontend_controller - .send_command(FrontendCommand::UpdateModuleList(module_list))?; + .send_command(FrontendCommand::ListModulesResult(module_list))?; } } @@ -503,7 +604,7 @@ fn worker_thread_routine( fn reconstruct_type_by_index_command<'p, T>( pdb_file: &PdbFile<'p, T>, - type_index: pdb::TypeIndex, + type_index: pdb_file::TypeIndex, primitives_flavor: PrimitiveReconstructionFlavor, print_header: bool, reconstruct_dependencies: bool, @@ -513,7 +614,7 @@ fn reconstruct_type_by_index_command<'p, T>( where T: io::Seek + io::Read + std::fmt::Debug + 'p, { - let (data, xrefs_from) = pdb_file.reconstruct_type_by_type_index( + let (data, xrefs_from) = pdb_file.reconstruct_type_by_index( type_index, primitives_flavor, reconstruct_dependencies, @@ -578,9 +679,27 @@ where } } +fn reconstruct_symbol_by_index_command<'p, T>( + pdb_file: &mut PdbFile<'p, T>, + symbol_index: pdb_file::SymbolIndex, + primitives_flavor: PrimitiveReconstructionFlavor, + print_header: bool, +) -> Result +where + T: io::Seek + io::Read + std::fmt::Debug + 'p, +{ + let data = pdb_file.reconstruct_symbol_by_index(symbol_index, primitives_flavor)?; + if print_header { + let file_header = generate_file_header(pdb_file, primitives_flavor, true, false); + Ok(format!("{file_header}{data}")) + } else { + Ok(data) + } +} + fn reconstruct_module_by_index_command<'p, T>( pdb_file: &mut PdbFile<'p, T>, - module_index: usize, + module_index: pdb_file::ModuleIndex, primitives_flavor: PrimitiveReconstructionFlavor, ignore_std_types: bool, print_header: bool, @@ -637,13 +756,13 @@ fn update_type_filter_command( use_regex: bool, ignore_std_types: bool, sort_by_index: bool, -) -> Vec<(String, pdb::TypeIndex)> +) -> TypeList where T: io::Seek + io::Read, { let filter_start = Instant::now(); - // Fitler out std types if needed + // Filter out std types if needed let filtered_type_list = if ignore_std_types { filter_std_types(&pdb_file.complete_type_list) } else { @@ -675,10 +794,10 @@ where /// Filter type list with a regular expression fn filter_types_regex( - type_list: &[(String, pdb::TypeIndex)], + type_list: &[(String, u32)], search_filter: &str, case_insensitive_search: bool, -) -> Vec<(String, pdb::TypeIndex)> { +) -> TypeList { match regex::RegexBuilder::new(search_filter) .case_insensitive(case_insensitive_search) .build() @@ -694,10 +813,10 @@ fn filter_types_regex( /// Filter type list with a plain (sub-)string fn filter_types_regular( - type_list: &[(String, pdb::TypeIndex)], + type_list: &[(String, u32)], search_filter: &str, case_insensitive_search: bool, -) -> Vec<(String, pdb::TypeIndex)> { +) -> TypeList { if case_insensitive_search { let search_filter = search_filter.to_lowercase(); par_iter_if_available!(type_list) @@ -713,13 +832,109 @@ fn filter_types_regular( } /// Filter type list to remove types in the `std` namespace -fn filter_std_types(type_list: &[(String, pdb::TypeIndex)]) -> Vec<(String, pdb::TypeIndex)> { +fn filter_std_types(type_list: &[(String, pdb_file::TypeIndex)]) -> TypeList { par_iter_if_available!(type_list) .filter(|r| !r.0.starts_with("std::")) .cloned() .collect() } +fn update_symbol_filter_command( + pdb_file: &PdbFile, + search_filter: &str, + case_insensitive_search: bool, + use_regex: bool, + ignore_std_symbols: bool, +) -> SymbolList +where + T: io::Seek + io::Read + fmt::Debug, +{ + let filter_start = Instant::now(); + + match pdb_file.symbol_list() { + Err(_) => SymbolList::default(), + Ok(symbol_list) => { + // Filter out std types if needed + let filtered_symbol_list = if ignore_std_symbols { + filter_std_symbols(&symbol_list) + } else { + symbol_list.clone() + }; + + let filtered_symbol_list = if search_filter.is_empty() { + // No need to filter + filtered_symbol_list + } else if use_regex { + filter_symbols_regex( + &filtered_symbol_list, + search_filter, + case_insensitive_search, + ) + } else { + filter_symbols_regular( + &filtered_symbol_list, + search_filter, + case_insensitive_search, + ) + }; + + log::debug!( + "Symbol filtering took {} ms", + filter_start.elapsed().as_millis() + ); + + filtered_symbol_list + } + } +} + +/// Filter symbol list to remove types in the `std` namespace +fn filter_std_symbols(symbol_list: &[(String, pdb_file::SymbolIndex)]) -> SymbolList { + par_iter_if_available!(symbol_list) + .filter(|r| !r.0.starts_with("std::")) + .cloned() + .collect() +} + +/// Filter type list with a regular expression +fn filter_symbols_regex( + symbol_list: &[(String, pdb_file::SymbolIndex)], + search_filter: &str, + case_insensitive_search: bool, +) -> SymbolList { + match regex::RegexBuilder::new(search_filter) + .case_insensitive(case_insensitive_search) + .build() + { + // In case of error, return an empty result + Err(_) => vec![], + Ok(regex) => par_iter_if_available!(symbol_list) + .filter(|r| regex.find(&r.0).is_some()) + .cloned() + .collect(), + } +} + +/// Filter type list with a plain (sub-)string +fn filter_symbols_regular( + symbol_list: &[(String, pdb_file::SymbolIndex)], + search_filter: &str, + case_insensitive_search: bool, +) -> SymbolList { + if case_insensitive_search { + let search_filter = search_filter.to_lowercase(); + par_iter_if_available!(symbol_list) + .filter(|r| r.0.to_lowercase().contains(&search_filter)) + .cloned() + .collect() + } else { + par_iter_if_available!(symbol_list) + .filter(|r| r.0.contains(search_filter)) + .cloned() + .collect() + } +} + fn list_modules_command<'p, T>( pdb_file: &PdbFile<'p, T>, search_filter: &str, @@ -797,8 +1012,8 @@ fn filter_modules_regular( fn list_type_xrefs_command<'p, T>( pdb_file: &PdbFile<'p, T>, - type_index: pdb::TypeIndex, -) -> Result> + type_index: pdb_file::TypeIndex, +) -> Result where T: io::Seek + io::Read + std::fmt::Debug + 'p, { diff --git a/resym_core/src/error.rs b/resym_core/src/error.rs index 6fab68f..09c607a 100644 --- a/resym_core/src/error.rs +++ b/resym_core/src/error.rs @@ -45,6 +45,11 @@ pub enum ResymCoreError { #[error("type not found: {0}")] TypeNameNotFoundError(String), + /// Error returned when querying for a symbol, that isn't present in + /// the PDB file. + #[error("symbol not found: {0}")] + SymbolNotFoundError(String), + /// Error returned when querying for a module by path, that isn't present in /// the PDB file. #[error("module not found: {0}")] diff --git a/resym_core/src/frontend.rs b/resym_core/src/frontend.rs index 2479eae..a81eb43 100644 --- a/resym_core/src/frontend.rs +++ b/resym_core/src/frontend.rs @@ -1,8 +1,10 @@ -use crate::{backend::PDBSlot, diffing::Diff, error::Result}; +use crate::{ + backend::PDBSlot, + diffing::Diff, + error::Result, + pdb_file::{ModuleList, SymbolList, TypeList}, +}; -pub type TypeIndex = pdb::TypeIndex; -pub type TypeList = Vec<(String, TypeIndex)>; -pub type ModuleList = Vec<(String, usize)>; /// Tuple containing the reconstructed type as a `String` /// and the list of directly referenced types as a `TypeList` pub type ReconstructedType = (String, TypeList); @@ -12,11 +14,22 @@ pub enum FrontendCommand { /// Send result from `LoadURL` backend command. /// Contains last path segment (i.e., file name) as a `String` and data as `Vec`. LoadURLResult(Result<(PDBSlot, String, Vec)>), + + // Types ListTypesResult(TypeList), ReconstructTypeResult(Result), + + // Symbols + ListSymbolsResult(SymbolList), + ReconstructSymbolResult(Result), + + // Modules + ListModulesResult(Result), ReconstructModuleResult(Result), - UpdateModuleList(Result), + + // Diff DiffResult(Result), + // Xrefs ListTypeCrossReferencesResult(Result), } diff --git a/resym_core/src/pdb_file.rs b/resym_core/src/pdb_file.rs index 11046b6..3568868 100644 --- a/resym_core/src/pdb_file.rs +++ b/resym_core/src/pdb_file.rs @@ -16,13 +16,23 @@ use std::{fs::File, path::Path, time::Instant}; use crate::{ error::{Result, ResymCoreError}, - frontend::{ModuleList, ReconstructedType, TypeList}, + frontend::ReconstructedType, par_iter_if_available, pdb_types::{ self, is_unnamed_type, type_name, DataFormatConfiguration, PrimitiveReconstructionFlavor, }, }; +pub type TypeIndex = u32; +pub type TypeList = Vec<(String, TypeIndex)>; +/// `SymbolIndex` have two parts: a module index and a symbol index +pub type SymbolIndex = (ModuleIndex, u32); +pub type SymbolList = Vec<(String, SymbolIndex)>; +pub type ModuleIndex = usize; +pub type ModuleList = Vec<(String, ModuleIndex)>; + +const GLOBAL_MODULE_INDEX: usize = usize::MAX; + /// Wrapper for different buffer types processed by `resym` #[derive(Debug)] pub enum PDBDataSource { @@ -55,13 +65,14 @@ pub struct PdbFile<'p, T> where T: io::Seek + io::Read + 'p, { - pub complete_type_list: Vec<(String, pdb::TypeIndex)>, + pub complete_type_list: Vec<(String, TypeIndex)>, pub forwarder_to_complete_type: Arc>, pub machine_type: pdb::MachineType, pub type_information: pdb::TypeInformation<'p>, pub debug_information: pdb::DebugInformation<'p>, + pub global_symbols: pdb::SymbolTable<'p>, pub file_path: PathBuf, - pub xref_to_map: RwLock>>, + pub xref_to_map: RwLock>>, pdb: RwLock>, } @@ -73,6 +84,7 @@ impl<'p> PdbFile<'p, File> { let mut pdb = pdb::PDB::open(file)?; let type_information = pdb.type_information()?; let debug_information = pdb.debug_information()?; + let global_symbols = pdb.global_symbols()?; let machine_type = pdb.debug_information()?.machine_type()?; let mut pdb_file = PdbFile { @@ -81,6 +93,7 @@ impl<'p> PdbFile<'p, File> { machine_type, type_information, debug_information, + global_symbols, file_path: pdb_file_path.to_owned(), xref_to_map: DashMap::default().into(), pdb: pdb.into(), @@ -101,6 +114,7 @@ impl<'p> PdbFile<'p, PDBDataSource> { let mut pdb = pdb::PDB::open(reader)?; let type_information = pdb.type_information()?; let debug_information = pdb.debug_information()?; + let global_symbols = pdb.global_symbols()?; let machine_type = pdb.debug_information()?.machine_type()?; let mut pdb_file = PdbFile { @@ -109,6 +123,7 @@ impl<'p> PdbFile<'p, PDBDataSource> { machine_type, type_information, debug_information, + global_symbols, file_path: pdb_file_name.into(), xref_to_map: DashMap::default().into(), pdb: pdb.into(), @@ -127,6 +142,7 @@ impl<'p> PdbFile<'p, PDBDataSource> { let mut pdb = pdb::PDB::open(reader)?; let type_information = pdb.type_information()?; let debug_information = pdb.debug_information()?; + let global_symbols = pdb.global_symbols()?; let machine_type = pdb.debug_information()?.machine_type()?; let mut pdb_file = PdbFile { @@ -135,6 +151,7 @@ impl<'p> PdbFile<'p, PDBDataSource> { machine_type, type_information, debug_information, + global_symbols, file_path: pdb_file_name.into(), xref_to_map: DashMap::default().into(), pdb: pdb.into(), @@ -178,7 +195,7 @@ where if is_unnamed_type(&class_name) { class_name = format!("_unnamed_{type_index}"); } - self.complete_type_list.push((class_name, type_index)); + self.complete_type_list.push((class_name, type_index.0)); } pdb::TypeData::Union(data) => { let mut class_name = data.name.to_string().into_owned(); @@ -194,7 +211,7 @@ where if is_unnamed_type(&class_name) { class_name = format!("_unnamed_{type_index}"); } - self.complete_type_list.push((class_name, type_index)); + self.complete_type_list.push((class_name, type_index.0)); } pdb::TypeData::Enumeration(data) => { let mut class_name = data.name.to_string().into_owned(); @@ -210,7 +227,7 @@ where if is_unnamed_type(&class_name) { class_name = format!("_unnamed_{type_index}"); } - self.complete_type_list.push((class_name, type_index)); + self.complete_type_list.push((class_name, type_index.0)); } _ => {} } @@ -245,7 +262,7 @@ where ignore_std_types: bool, ) -> Result { // Populate our `TypeFinder` and find the right type index - let mut type_index = pdb::TypeIndex::default(); + let mut type_index = TypeIndex::default(); let mut type_finder = self.type_information.finder(); { let mut type_iter = self.type_information.iter(); @@ -265,13 +282,13 @@ where let class_name = data.name.to_string(); if is_unnamed_type(&class_name) { if type_name == format!("_unnamed_{item_type_index}") { - type_index = item_type_index; + type_index = item_type_index.0; } } else if class_name == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } else if let Some(unique_name) = data.unique_name { if unique_name.to_string() == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } } } @@ -285,13 +302,13 @@ where let union_name = data.name.to_string(); if is_unnamed_type(&union_name) { if type_name == format!("_unnamed_{item_type_index}") { - type_index = item_type_index; + type_index = item_type_index.0; } } else if data.name.to_string() == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } else if let Some(unique_name) = data.unique_name { if unique_name.to_string() == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } } } @@ -305,13 +322,13 @@ where let enum_name = data.name.to_string(); if is_unnamed_type(&enum_name) { if type_name == format!("_unnamed_{item_type_index}") { - type_index = item_type_index; + type_index = item_type_index.0; } } else if data.name.to_string() == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } else if let Some(unique_name) = data.unique_name { if unique_name.to_string() == type_name { - type_index = item_type_index; + type_index = item_type_index.0; } } } @@ -322,7 +339,7 @@ where } } - if type_index == pdb::TypeIndex::default() { + if type_index == TypeIndex::default() { Err(ResymCoreError::TypeNameNotFoundError(type_name.to_owned())) } else { self.reconstruct_type_by_type_index_internal( @@ -336,9 +353,9 @@ where } } - pub fn reconstruct_type_by_type_index( + pub fn reconstruct_type_by_index( &self, - type_index: pdb::TypeIndex, + type_index: TypeIndex, primitives_flavor: PrimitiveReconstructionFlavor, reconstruct_dependencies: bool, print_access_specifiers: bool, @@ -363,6 +380,48 @@ where ) } + pub fn symbol_list(&self) -> Result { + let mut symbols = Vec::new(); + let mut symbol_name_set = HashSet::new(); + + // Modules' private symbols + let mut modules = self.debug_information.modules()?.enumerate(); + { + let mut pdb = self.pdb.write().expect("lock shouldn't be poisoned"); + while let Some((module_index, module)) = modules.next()? { + let module_info = match pdb.module_info(&module)? { + Some(info) => info, + None => { + continue; + } + }; + + let mut module_symbols = module_info.symbols()?; + while let Some(symbol) = module_symbols.next()? { + if let Some(symbol_name) = symbol_name(&symbol) { + if !symbol_name_set.contains(&symbol_name) { + symbols.push((symbol_name.clone(), (module_index, symbol.index().0))); + symbol_name_set.insert(symbol_name); + } + } + } + } + } + + // Global symbols + let mut symbol_table = self.global_symbols.iter(); + while let Some(symbol) = symbol_table.next()? { + if let Some(symbol_name) = symbol_name(&symbol) { + if !symbol_name_set.contains(&symbol_name) { + symbols.push((symbol_name.clone(), (GLOBAL_MODULE_INDEX, symbol.index().0))); + symbol_name_set.insert(symbol_name); + } + } + } + + Ok(symbols) + } + pub fn module_list(&self) -> Result { let module_list = self .debug_information @@ -373,6 +432,52 @@ where Ok(module_list.collect()?) } + pub fn reconstruct_symbol_by_index( + &self, + symbol_index: SymbolIndex, + primitives_flavor: PrimitiveReconstructionFlavor, + ) -> Result { + // Populate our `TypeFinder` + let mut type_finder = self.type_information.finder(); + { + let mut type_iter = self.type_information.iter(); + while (type_iter.next()?).is_some() { + type_finder.update(&type_iter); + } + } + + // Check which module the symbol is from + if symbol_index.0 == GLOBAL_MODULE_INDEX { + // Global symbols + let mut symbol_table = self.global_symbols.iter(); + while let Some(symbol) = symbol_table.next()? { + if symbol.index().0 == symbol_index.1 { + return Ok(self + .reconstruct_symbol(&type_finder, &symbol, primitives_flavor) + .unwrap_or_default()); + } + } + } else if let Some(module) = self.debug_information.modules()?.nth(symbol_index.0)? { + // Modules' private symbols + let mut pdb = self.pdb.write().expect("lock shouldn't be poisoned"); + if let Some(module_info) = pdb.module_info(&module)? { + let mut module_symbols = module_info.symbols_at(symbol_index.1.into())?; + while let Some(symbol) = module_symbols.next()? { + if symbol.index().0 == symbol_index.1 { + return Ok(self + .reconstruct_symbol(&type_finder, &symbol, primitives_flavor) + .unwrap_or_default()); + } + } + } + } + + Err(ResymCoreError::SymbolNotFoundError(format!( + "Symbol #{:?} not found", + symbol_index + ))) + } + pub fn reconstruct_module_by_path( &self, module_path: &str, @@ -424,80 +529,11 @@ where let mut result = String::default(); module_info.symbols()?.for_each(|symbol| { - let mut needed_types = pdb_types::NeededTypeSet::new(); - - match symbol.parse()? { - pdb::SymbolData::UserDefinedType(udt) => { - if let Ok(type_name) = type_name( - &type_finder, - &self.forwarder_to_complete_type, - udt.type_index, - &primitives_flavor, - &mut needed_types, - ) { - if type_name.0 == "..." { - // No type - result += - format!("{}; // Missing type information\n", udt.name).as_str(); - } else { - result += - format!("using {} = {}{};\n", udt.name, type_name.0, type_name.1) - .as_str(); - } - } - } - pdb::SymbolData::Procedure(procedure) => { - if let Ok(type_name) = type_name( - &type_finder, - &self.forwarder_to_complete_type, - procedure.type_index, - &primitives_flavor, - &mut needed_types, - ) { - if type_name.0 == "..." { - // No type - result += format!( - "void {}(); // CodeSize={} (missing type information)\n", - procedure.name, procedure.len - ) - .as_str(); - } else { - result += format!( - "{}{}{}; // CodeSize={}\n", - type_name.0, procedure.name, type_name.1, procedure.len - ) - .as_str(); - } - } - } - pdb::SymbolData::Data(data) => { - if let Ok(type_name) = type_name( - &type_finder, - &self.forwarder_to_complete_type, - data.type_index, - &primitives_flavor, - &mut needed_types, - ) { - if type_name.0 == "..." { - // No type - result += - format!("{}; // Missing type information\n", data.name).as_str(); - } else { - result += - format!("{} {}{};\n", type_name.0, data.name, type_name.1).as_str(); - } - } - } - pdb::SymbolData::UsingNamespace(namespace) => { - result += format!("using namespace {};\n", namespace.name).as_str(); - } - pdb::SymbolData::AnnotationReference(annotation) => { - // TODO(ergrelet): update when support for annotations - // (symbol kind 0x1019) has been implemented in `pdb` - result += format!("__annotation(); // {}\n", annotation.name).as_str(); - } - // Ignore - _ => {} + let reconstructed_symbol = + self.reconstruct_symbol(&type_finder, &symbol, primitives_flavor); + if let Some(reconstructed_symbol) = reconstructed_symbol { + result += &reconstructed_symbol; + result.push('\n'); } Ok(()) @@ -509,7 +545,7 @@ where fn reconstruct_type_by_type_index_internal( &self, type_finder: &pdb::TypeFinder, - type_index: pdb::TypeIndex, + type_index: TypeIndex, primitives_flavor: PrimitiveReconstructionFlavor, reconstruct_dependencies: bool, print_access_specifiers: bool, @@ -526,7 +562,7 @@ where type_data.add( type_finder, &self.forwarder_to_complete_type, - type_index, + type_index.into(), &primitives_flavor, &mut needed_types, )?; @@ -537,7 +573,7 @@ where &Default::default(), &mut reconstruction_output, )?; - let needed_types: Vec = needed_types.into_iter().map(|e| e.0).collect(); + let needed_types: Vec = needed_types.into_iter().map(|e| e.0 .0).collect(); let xrefs_from = self.type_list_from_type_indices(&needed_types); return Ok((reconstruction_output, xrefs_from)); @@ -545,13 +581,12 @@ where let mut xrefs_from = vec![]; // Add all the needed types iteratively until we're done - let mut type_dependency_map: HashMap> = - HashMap::new(); + 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 types_to_process: VecDeque = VecDeque::from([type_index]); let mut processed_type_set = HashSet::new(); // Keep processing new types until there's nothing to process while let Some(needed_type_index) = types_to_process.pop_front() { @@ -565,34 +600,35 @@ where type_data.add( type_finder, &self.forwarder_to_complete_type, - needed_type_index, + needed_type_index.into(), &primitives_flavor, &mut needed_types, )?; // Initialize only once, the first time (i.e., for the requested type) if xrefs_from.is_empty() { - let needed_types: Vec = - needed_types.iter().map(|e| e.0).collect(); + let needed_types: Vec = + needed_types.iter().map(|e| e.0 .0).collect(); xrefs_from = self.type_list_from_type_indices(&needed_types); } - for pair in &needed_types { + for (type_index, is_pointer) in &needed_types { // Add forward declaration for types referenced by pointers - if pair.1 { - type_data.add_as_forward_declaration(type_finder, pair.0)?; + if *is_pointer { + type_data.add_as_forward_declaration(type_finder, *type_index)?; } // Update type dependency map if let Some(type_dependency) = type_dependency_map.get_mut(&needed_type_index) { - type_dependency.push(*pair); + type_dependency.push((type_index.0, *is_pointer)); } else { - type_dependency_map.insert(needed_type_index, vec![*pair]); + type_dependency_map + .insert(needed_type_index, vec![(type_index.0, *is_pointer)]); } } // 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)); + types_to_process.extend(needed_types.into_iter().map(|pair| pair.0 .0)); } log::debug!( @@ -622,8 +658,7 @@ where ) -> Result { let mut type_data = pdb_types::Data::new(ignore_std_types); let mut processed_types = Vec::new(); - let mut type_dependency_map: HashMap> = - HashMap::new(); + let mut type_dependency_map: HashMap> = HashMap::new(); { let mut type_finder = self.type_information.finder(); // Populate our `TypeFinder` @@ -665,20 +700,21 @@ where } } else { // Handle success - processed_types.push(complete_type_index); - for pair in &needed_types { + processed_types.push(complete_type_index.0); + for (type_index, is_pointer) in &needed_types { // Add forward declaration for types referenced by pointers - if pair.1 { - type_data.add_as_forward_declaration(&type_finder, pair.0)?; + if *is_pointer { + type_data.add_as_forward_declaration(&type_finder, *type_index)?; } // Update type dependency map if let Some(type_dependency) = - type_dependency_map.get_mut(&complete_type_index) + type_dependency_map.get_mut(&complete_type_index.0) { - type_dependency.push(*pair); + type_dependency.push((type_index.0, *is_pointer)); } else { - type_dependency_map.insert(complete_type_index, vec![*pair]); + type_dependency_map + .insert(complete_type_index.0, vec![(type_index.0, *is_pointer)]); } } } @@ -700,10 +736,7 @@ where Ok(reconstruction_output) } - pub fn get_xrefs_for_type( - &self, - type_index: pdb::TypeIndex, - ) -> Result> { + pub fn get_xrefs_for_type(&self, type_index: TypeIndex) -> Result { // Generate xref cache if empty if self .xref_to_map @@ -721,7 +754,7 @@ where } // Iterate through all types - let xref_map: DashMap> = DashMap::default(); + let xref_map: DashMap> = DashMap::default(); let mut type_iter = self.type_information.iter(); while let Some(type_item) = type_iter.next()? { let current_type_index = type_item.index(); @@ -751,10 +784,10 @@ where } 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); + if let Some(mut xref_list) = xref_map.get_mut(&t.0) { + xref_list.push(current_type_index.0); } else { - xref_map.insert(*t, vec![current_type_index]); + xref_map.insert(t.0, vec![current_type_index.0]); } }); } @@ -782,7 +815,7 @@ where } } - fn type_list_from_type_indices(&self, type_indices: &[pdb::TypeIndex]) -> TypeList { + fn type_list_from_type_indices(&self, type_indices: &[TypeIndex]) -> TypeList { par_iter_if_available!(self.complete_type_list) .filter_map(|(type_name, type_index)| { if type_indices.contains(type_index) { @@ -793,18 +826,136 @@ where }) .collect() } + + fn reconstruct_symbol( + &self, + type_finder: &pdb::ItemFinder<'_, pdb::TypeIndex>, + symbol: &pdb::Symbol<'_>, + primitives_flavor: PrimitiveReconstructionFlavor, + ) -> Option { + let mut needed_types = pdb_types::NeededTypeSet::new(); + match symbol.parse().ok()? { + pdb::SymbolData::UserDefinedType(udt) => { + if let Ok(type_name) = type_name( + type_finder, + &self.forwarder_to_complete_type, + udt.type_index, + &primitives_flavor, + &mut needed_types, + ) { + if type_name.0 == "..." { + // No type + Some(format!("char {}; // Missing type information", udt.name)) + } else { + Some(format!( + "using {} = {}{};", + udt.name, type_name.0, type_name.1 + )) + } + } else { + None + } + } + + // Functions and methods + pdb::SymbolData::Procedure(procedure) => { + if let Ok(type_name) = type_name( + type_finder, + &self.forwarder_to_complete_type, + procedure.type_index, + &primitives_flavor, + &mut needed_types, + ) { + if type_name.0 == "..." { + // No type + Some(format!( + "void {}(); // CodeSize={} (missing type information)", + procedure.name, procedure.len + )) + } else { + Some(format!( + "{}{}{}; // CodeSize={}", + type_name.0, procedure.name, type_name.1, procedure.len + )) + } + } else { + None + } + } + + // Global variables + pdb::SymbolData::Data(data) => { + if let Ok(type_name) = type_name( + type_finder, + &self.forwarder_to_complete_type, + data.type_index, + &primitives_flavor, + &mut needed_types, + ) { + if type_name.0 == "..." { + // No type + Some(format!("char {}; // Missing type information", data.name)) + } else { + Some(format!("{} {}{};", type_name.0, data.name, type_name.1)) + } + } else { + None + } + } + + pdb::SymbolData::UsingNamespace(namespace) => { + Some(format!("using namespace {};", namespace.name)) + } + + pdb::SymbolData::AnnotationReference(annotation) => { + // TODO(ergrelet): update when support for annotations + // (symbol kind 0x1019) has been implemented in `pdb` + Some(format!("__annotation(); // {}", annotation.name)) + } + + // Public symbols + pdb::SymbolData::Public(data) => Some(if data.function { + // Add parenthese to distinguish functions from global variables + format!( + "void {}(); // Public symbol (no type information)", + data.name + ) + } else { + format!("char {}; // Public symbol (no type information)", data.name) + }), + + // Exported symbols + pdb::SymbolData::Export(data) => Some(if data.flags.data { + format!( + "char {}; // Exported symbol (no type information)", + data.name + ) + } else { + // Add parenthese to distinguish functions from exported variables + format!( + "void {}(); // Exported symbol (no type information)", + data.name + ) + }), + + _ => { + // ignore everything else + None + } + } + } } fn compute_type_depth_map( - type_dependency_map: &HashMap>, - root_types: &[pdb::TypeIndex], + type_dependency_map: &HashMap>, + root_types: &[TypeIndex], ) -> BTreeMap> { let depth_start = Instant::now(); - let mut type_depth_map: HashMap = + 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)> = + let mut types_to_visit: VecDeque<(usize, 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) { @@ -828,9 +979,9 @@ fn compute_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); + type_indices.push(type_index.into()); } else { - acc.insert(type_depth, vec![type_index]); + acc.insert(type_depth, vec![type_index.into()]); } acc @@ -843,3 +994,26 @@ fn compute_type_depth_map( inverted_type_depth_map } + +fn symbol_name(symbol: &pdb::Symbol) -> Option { + match symbol.parse().ok()? { + pdb::SymbolData::UserDefinedType(udt) => Some(udt.name.to_string().to_string()), + + // Functions and methods + pdb::SymbolData::Procedure(procedure) => Some(procedure.name.to_string().to_string()), + + // Global variables + pdb::SymbolData::Data(data) => Some(data.name.to_string().to_string()), + + // Public symbols + pdb::SymbolData::Public(data) => Some(data.name.to_string().to_string()), + + // Expored symbols + pdb::SymbolData::Export(data) => Some(data.name.to_string().to_string()), + + _ => { + // ignore everything else + None + } + } +} diff --git a/resymc/src/resymc_app.rs b/resymc/src/resymc_app.rs index 076d7f0..04a1abe 100644 --- a/resymc/src/resymc_app.rs +++ b/resymc/src/resymc_app.rs @@ -284,7 +284,7 @@ impl ResymcApp { use_regex, ))?; // Wait for the backend to finish listing modules - if let FrontendCommand::UpdateModuleList(module_list_result) = + if let FrontendCommand::ListModulesResult(module_list_result) = self.frontend_controller.rx_ui.recv()? { // Dump output