Skip to content

Commit

Permalink
WIP: Implement cross-reference listing
Browse files Browse the repository at this point in the history
  • Loading branch information
ergrelet committed Mar 9, 2024
1 parent 164900d commit ca80a18
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 17 deletions.
110 changes: 95 additions & 15 deletions resym/src/resym_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,36 @@ impl From<ResymPDBSlots> for PDBSlot {
}
}

/// Tabs available for the left-side panel
#[derive(PartialEq)]
enum ExplorerTab {
enum LeftPanelTab {
TypeSearch,
ModuleBrowsing,
}

/// Tabs available for the bottom panel
#[derive(PartialEq)]
enum BottomPanelTab {
Console,
Xrefs,
}

/// Struct that represents our GUI application.
/// It contains the whole application's context at all time.
pub struct ResymApp {
current_mode: ResymAppMode,
explorer_selected_tab: ExplorerTab,
// Components used in the left-side panel
left_panel_selected_tab: LeftPanelTab,
type_search: TextSearchComponent,
type_list: TypeListComponent,
module_search: TextSearchComponent,
module_tree: ModuleTreeComponent,
code_view: CodeViewComponent,
// Components used in the bottom panel
bottom_panel_selected_tab: BottomPanelTab,
console: ConsoleComponent,
xref_list: TypeListComponent,
// Other components
settings: SettingsComponent,
#[cfg(feature = "http")]
open_url: OpenURLComponent,
Expand Down Expand Up @@ -141,13 +154,15 @@ impl ResymApp {
log::info!("{} {}", PKG_NAME, PKG_VERSION);
Ok(Self {
current_mode: ResymAppMode::Idle,
explorer_selected_tab: ExplorerTab::TypeSearch,
left_panel_selected_tab: LeftPanelTab::TypeSearch,
type_search: TextSearchComponent::new(),
type_list: TypeListComponent::new(),
module_search: TextSearchComponent::new(),
module_tree: ModuleTreeComponent::new(),
code_view: CodeViewComponent::new(),
bottom_panel_selected_tab: BottomPanelTab::Console,
console: ConsoleComponent::new(logger),
xref_list: TypeListComponent::new(),
settings: SettingsComponent::new(app_settings),
#[cfg(feature = "http")]
open_url: OpenURLComponent::new(),
Expand Down Expand Up @@ -184,20 +199,20 @@ impl ResymApp {
.show(ctx, |ui| {
ui.horizontal(|ui| {
ui.selectable_value(
&mut self.explorer_selected_tab,
ExplorerTab::TypeSearch,
&mut self.left_panel_selected_tab,
LeftPanelTab::TypeSearch,
"Search types",
);
ui.selectable_value(
&mut self.explorer_selected_tab,
ExplorerTab::ModuleBrowsing,
&mut self.left_panel_selected_tab,
LeftPanelTab::ModuleBrowsing,
"Browse modules",
);
});
ui.separator();

match self.explorer_selected_tab {
ExplorerTab::TypeSearch => {
match self.left_panel_selected_tab {
LeftPanelTab::TypeSearch => {
// Callback run when the search query changes
let on_query_update = |search_query: &str| {
// Update filtered list if filter has changed
Expand Down Expand Up @@ -239,7 +254,7 @@ impl ResymApp {
ui,
);
}
ExplorerTab::ModuleBrowsing => {
LeftPanelTab::ModuleBrowsing => {
// Callback run when the search query changes
let on_query_update = |search_query: &str| match self.current_mode {
ResymAppMode::Browsing(..) | ResymAppMode::Comparing(..) => {
Expand Down Expand Up @@ -305,18 +320,44 @@ impl ResymApp {
});
}

/// Update/render the bottom panel component and its sub-components
fn update_bottom_panel(&mut self, ctx: &egui::Context) {
egui::TopBottomPanel::bottom("bottom_panel")
.min_height(100.0)
.resizable(true)
.show(ctx, |ui| {
// Console panel
ui.vertical(|ui| {
ui.label("Console");
// Tab headers
ui.horizontal(|ui| {
ui.selectable_value(
&mut self.bottom_panel_selected_tab,
BottomPanelTab::Console,
"Console",
);
ui.selectable_value(
&mut self.bottom_panel_selected_tab,
BottomPanelTab::Xrefs,
"Xrefs to",
);
});
ui.add_space(4.0);

// Update the console component
self.console.update(ui);
// Tab body
match self.bottom_panel_selected_tab {
BottomPanelTab::Console => {
// Console panel
self.console.update(ui);
}
BottomPanelTab::Xrefs => {
// Update Xref list
self.xref_list.update(
&self.settings.app_settings,
&self.current_mode,
&self.backend,
ui,
);
}
}
});
});
}
Expand All @@ -335,13 +376,35 @@ impl ResymApp {
// Start displaying buttons from the right
#[cfg_attr(target_arch = "wasm32", allow(unused_variables))]
ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
// Fetures only available in "Browsing" mode
if let ResymAppMode::Browsing(..) = self.current_mode {
// Save button handling
// Save button
// Note: not available on wasm32
#[cfg(not(target_arch = "wasm32"))]
if ui.button("💾 Save (Ctrl+S)").clicked() {
self.start_save_reconstruted_content();
}

// Cross-references button
if let Some(selected_type_index) = self.type_list.selected_type_index() {
if ui.button("🔍 Find Xrefs to").clicked() {
log::info!(
"Looking for cross-references for type #0x{:x}...",
selected_type_index.0
);
if let Err(err) = self.backend.send_command(
BackendCommand::ListTypeCrossReferences(
ResymPDBSlots::Main as usize,
selected_type_index,
),
) {
log::error!(
"Failed to list cross-references to type #0x{:x}: {err}",
selected_type_index.0
);
}
}
}
}
});
});
Expand Down Expand Up @@ -598,6 +661,23 @@ impl ResymApp {
FrontendCommand::UpdateFilteredTypes(filtered_types) => {
self.type_list.update_type_list(filtered_types);
}

FrontendCommand::ListTypeCrossReferencesResult(xref_list_result) => {
match xref_list_result {
Err(err) => {
log::error!("Failed to list cross-references: {err}");
}
Ok(xref_list) => {
let xref_count = xref_list.len();
log::info!("{xref_count} cross-references found!");

// Update xref list component
self.xref_list.update_type_list(xref_list);
// Switch to xref tab
self.bottom_panel_selected_tab = BottomPanelTab::Xrefs;
}
}
}
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion resym/src/ui_components/type_list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use eframe::egui::{self, ScrollArea, TextStyle};
use resym_core::{
backend::{Backend, BackendCommand},
frontend::TypeList,
frontend::{TypeIndex, TypeList},
};

use crate::{mode::ResymAppMode, resym_app::ResymPDBSlots, settings::ResymAppSettings};
Expand All @@ -19,6 +19,12 @@ impl TypeListComponent {
}
}

pub fn selected_type_index(&self) -> Option<TypeIndex> {
self.filtered_type_list
.get(self.selected_row)
.map(|tuple| tuple.1)
}

pub fn update_type_list(&mut self, type_list: TypeList) {
self.filtered_type_list = type_list;
self.selected_row = usize::MAX;
Expand All @@ -37,6 +43,12 @@ impl TypeListComponent {
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 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
16 changes: 16 additions & 0 deletions resym_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,19 @@ macro_rules! par_sort_by_if_available {
$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.find($($x)*)
};
}
#[macro_export]
#[cfg(feature = "rayon")]
macro_rules! find_any_if_available {
($expression:expr, $($x:tt)*) => {
$expression.find_any($($x)*)
};
}
Loading

0 comments on commit ca80a18

Please sign in to comment.