Skip to content

Commit

Permalink
Merge pull request #55 from ergrelet/52-implement-a-way-to-find-cross…
Browse files Browse the repository at this point in the history
…-references-for-a-type

Implement a way to find cross-references for a type
  • Loading branch information
ergrelet authored Mar 10, 2024
2 parents 164900d + d04a07f commit 0ee1e17
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 102 deletions.
213 changes: 188 additions & 25 deletions resym/src/resym_app.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion resym/src/ui_components/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ impl ConsoleComponent {
ui.add(
egui::TextEdit::singleline(&mut self.content[row_index].as_str())
.font(TEXT_STYLE)
.clip_text(false),
.clip_text(false)
.desired_width(f32::INFINITY),
);
}
},
Expand Down
52 changes: 10 additions & 42 deletions resym/src/ui_components/type_list.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use eframe::egui::{self, ScrollArea, TextStyle};
use resym_core::{
backend::{Backend, BackendCommand},
frontend::TypeList,
};

use crate::{mode::ResymAppMode, resym_app::ResymPDBSlots, settings::ResymAppSettings};
use resym_core::frontend::{TypeIndex, TypeList};

pub struct TypeListComponent {
filtered_type_list: TypeList,
Expand All @@ -24,19 +19,23 @@ impl TypeListComponent {
self.selected_row = usize::MAX;
}

pub fn update(
pub fn update<CB: FnMut(&str, TypeIndex)>(
&mut self,
app_settings: &ResymAppSettings,
current_mode: &ResymAppMode,
backend: &Backend,
ui: &mut egui::Ui,
on_type_selected: &mut CB,
) {
let num_rows = self.filtered_type_list.len();
const TEXT_STYLE: TextStyle = TextStyle::Body;
let row_height = ui.text_style_height(&TEXT_STYLE);
ui.with_layout(
egui::Layout::top_down(egui::Align::Min).with_cross_justify(true),
|ui| {
if num_rows == 0 {
// Display a default message to make it obvious the list is empty
ui.label("No results");
return;
}

ScrollArea::vertical()
.auto_shrink([false, false])
.show_rows(ui, row_height, num_rows, |ui, row_range| {
Expand All @@ -48,38 +47,7 @@ impl TypeListComponent {
.clicked()
{
self.selected_row = row_index;
match current_mode {
ResymAppMode::Browsing(..) => {
if let Err(err) = backend.send_command(
BackendCommand::ReconstructTypeByIndex(
ResymPDBSlots::Main as usize,
*type_index,
app_settings.primitive_types_flavor,
app_settings.print_header,
app_settings.reconstruct_dependencies,
app_settings.print_access_specifiers,
),
) {
log::error!("Failed to reconstruct type: {}", err);
}
}
ResymAppMode::Comparing(..) => {
if let Err(err) =
backend.send_command(BackendCommand::DiffTypeByName(
ResymPDBSlots::Main as usize,
ResymPDBSlots::Diff as usize,
type_name.clone(),
app_settings.primitive_types_flavor,
app_settings.print_header,
app_settings.reconstruct_dependencies,
app_settings.print_access_specifiers,
))
{
log::error!("Failed to reconstruct type diff: {}", err);
}
}
_ => log::error!("Invalid application state"),
}
on_type_selected(type_name, *type_index);
}
}
});
Expand Down
27 changes: 27 additions & 0 deletions resym_core/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub enum BackendCommand {
PrimitiveReconstructionFlavor,
bool,
),
/// Retrieve a list of all types that reference the given type
ListTypeCrossReferences(PDBSlot, pdb::TypeIndex),
}

/// Struct that represents the backend. The backend is responsible
Expand Down Expand Up @@ -481,6 +483,14 @@ fn worker_thread_routine(
}
}
}

BackendCommand::ListTypeCrossReferences(pdb_slot, type_index) => {
if let Some(pdb_file) = pdb_files.get(&pdb_slot) {
let xref_list = list_type_xrefs_command(&mut pdb_file.borrow_mut(), type_index);
frontend_controller
.send_command(FrontendCommand::ListTypeCrossReferencesResult(xref_list))?;
}
}
}
}

Expand Down Expand Up @@ -757,3 +767,20 @@ fn filter_modules_regular(
.collect()
}
}

fn list_type_xrefs_command<'p, T>(
pdb_file: &mut PdbFile<'p, T>,
type_index: pdb::TypeIndex,
) -> Result<Vec<(String, pdb::TypeIndex)>>
where
T: io::Seek + io::Read + std::fmt::Debug + 'p,
{
let xref_start = Instant::now();
let xref_list = pdb_file.get_xrefs_for_type(type_index)?;
log::debug!(
"Xref resolution took {} ms",
xref_start.elapsed().as_millis()
);

Ok(xref_list)
}
4 changes: 3 additions & 1 deletion resym_core/src/frontend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{backend::PDBSlot, diffing::Diff, error::Result};

pub type TypeList = Vec<(String, pdb::TypeIndex)>;
pub type TypeIndex = pdb::TypeIndex;
pub type TypeList = Vec<(String, TypeIndex)>;
pub type ModuleList = Vec<(String, usize)>;

pub enum FrontendCommand {
Expand All @@ -13,6 +14,7 @@ pub enum FrontendCommand {
ReconstructModuleResult(Result<String>),
UpdateModuleList(Result<ModuleList>),
DiffResult(Result<Diff>),
ListTypeCrossReferencesResult(Result<TypeList>),
}

pub trait FrontendController {
Expand Down
33 changes: 1 addition & 32 deletions resym_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,9 @@ mod error;
pub mod frontend;
pub mod pdb_file;
pub mod pdb_types;
pub mod rayon_utils;
pub mod syntax_highlighting;

pub use error::*;

const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Macro used to switch between iterators depending on rayon's availability
#[macro_export]
#[cfg(not(feature = "rayon"))]
macro_rules! par_iter_if_available {
($expression:expr) => {
$expression.iter()
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! par_iter_if_available {
($expression:expr) => {
$expression.par_iter()
};
}

/// Macro used to switch between functions depending on rayon's availability
#[macro_export]
#[cfg(not(feature = "rayon"))]
macro_rules! par_sort_by_if_available {
($expression:expr, $($x:tt)*) => {
$expression.sort_by($($x)*)
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! par_sort_by_if_available {
($expression:expr, $($x:tt)*) => {
$expression.par_sort_by($($x)*)
};
}
73 changes: 73 additions & 0 deletions resym_core/src/pdb_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::{fs::File, path::Path, time::Instant};

use crate::{
error::{Result, ResymCoreError},
find_any_if_available,
frontend::ModuleList,
par_iter_if_available,
pdb_types::{
Expand Down Expand Up @@ -61,6 +62,7 @@ where
pub type_information: pdb::TypeInformation<'p>,
pub debug_information: pdb::DebugInformation<'p>,
pub file_path: PathBuf,
pub xref_map: DashMap<pdb::TypeIndex, Vec<pdb::TypeIndex>>,
_pdb: pdb::PDB<'p, T>,
}

Expand All @@ -81,6 +83,7 @@ impl<'p> PdbFile<'p, File> {
type_information,
debug_information,
file_path: pdb_file_path.to_owned(),
xref_map: DashMap::default(),
_pdb: pdb,
};
pdb_file.load_symbols()?;
Expand Down Expand Up @@ -108,6 +111,7 @@ impl<'p> PdbFile<'p, PDBDataSource> {
type_information,
debug_information,
file_path: pdb_file_name.into(),
xref_map: DashMap::default(),
_pdb: pdb,
};
pdb_file.load_symbols()?;
Expand All @@ -133,6 +137,7 @@ impl<'p> PdbFile<'p, PDBDataSource> {
type_information,
debug_information,
file_path: pdb_file_name.into(),
xref_map: DashMap::default(),
_pdb: pdb,
};
pdb_file.load_symbols()?;
Expand Down Expand Up @@ -604,4 +609,72 @@ where
type_data.reconstruct(&fmt_configuration, &mut reconstruction_output)?;
Ok(reconstruction_output)
}

pub fn get_xrefs_for_type(
&mut self,
type_index: pdb::TypeIndex,
) -> Result<Vec<(String, pdb::TypeIndex)>> {
// Generate xref cache if empty
if self.xref_map.is_empty() {
// 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);
}
}

// Iterate through all types
let xref_map: DashMap<pdb::TypeIndex, Vec<pdb::TypeIndex>> = 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();
// Reconstruct type and retrieve referenced types
let mut type_data = pdb_types::Data::new();
let mut needed_types = pdb_types::TypeSet::new();
type_data.add(
&type_finder,
&self.forwarder_to_complete_type,
current_type_index,
&PrimitiveReconstructionFlavor::Raw,
&mut needed_types,
)?;

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 {
xref_map.insert(*t, vec![current_type_index]);
}
});
}

// Update cache
self.xref_map = xref_map;
}

// Query xref cache
if let Some(xref_list) = self.xref_map.get(&type_index) {
// Convert the xref list into a proper Name+TypeIndex tuple list
let xref_type_list = xref_list
.iter()
.map(|xref_type_index| {
// Look for the corresponding tuple (in parallel if possible)
let tuple = find_any_if_available!(
self.complete_type_list,
|(_, type_index)| type_index == xref_type_index
)
.expect("`complete_type_list` should contain type index");

tuple.clone()
})
.collect();

Ok(xref_type_list)
} else {
// No xrefs found for the given type
Ok(vec![])
}
}
}
2 changes: 1 addition & 1 deletion resym_core/src/pdb_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ impl<'p> Data<'p> {
}

// ignore
other => log::warn!("don't know how to add {:?}", other),
other => log::debug!("don't know how to add {:?}", other),
}

Ok(())
Expand Down
47 changes: 47 additions & 0 deletions resym_core/src/rayon_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/// Macro used to switch between iterators depending on rayon's availability
#[macro_export]
#[cfg(not(feature = "rayon"))]
macro_rules! par_iter_if_available {
($expression:expr) => {
$expression.iter()
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! par_iter_if_available {
($expression:expr) => {
$expression.par_iter()
};
}

/// Macro used to switch between functions depending on rayon's availability
#[macro_export]
#[cfg(not(feature = "rayon"))]
macro_rules! par_sort_by_if_available {
($expression:expr, $($x:tt)*) => {
$expression.sort_by($($x)*)
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! par_sort_by_if_available {
($expression:expr, $($x:tt)*) => {
$expression.par_sort_by($($x)*)
};
}

/// Macro used to switch between `find_any` and `find` depending on rayon's availability
#[macro_export]
#[cfg(not(feature = "rayon"))]
macro_rules! find_any_if_available {
($expression:expr, $($x:tt)*) => {
$expression.iter().find($($x)*)
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! find_any_if_available {
($expression:expr, $($x:tt)*) => {
$expression.par_iter().find_any($($x)*)
};
}

0 comments on commit 0ee1e17

Please sign in to comment.