Skip to content

Commit

Permalink
WIP: Implement a symbol reconstruction feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ergrelet committed Mar 28, 2024
1 parent 425a277 commit 5f7212e
Show file tree
Hide file tree
Showing 9 changed files with 702 additions and 213 deletions.
185 changes: 158 additions & 27 deletions resym/src/resym_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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,
},
};

Expand All @@ -44,6 +45,7 @@ impl From<ResymPDBSlots> for PDBSlot {
#[derive(PartialEq)]
enum LeftPanelTab {
TypeSearch,
SymbolSearch,
ModuleBrowsing,
}

Expand All @@ -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<TypeIndex>,
selected_type_index: Option<TypeIndex>,
symbol_search: TextSearchComponent,
symbol_list: IndexListComponent<SymbolIndex>,
selected_symbol_index: Option<SymbolIndex>,
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<TypeIndex>,
xref_from_list: IndexListComponent<TypeIndex>,
// Other components
settings: SettingsComponent,
selected_type_index: Option<TypeIndex>,
#[cfg(feature = "http")]
open_url: OpenURLComponent,
frontend_controller: Arc<EguiFrontendController>,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -296,6 +309,80 @@ 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,
))
} 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,
))
};
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 {
Expand Down Expand Up @@ -557,8 +644,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(
Expand All @@ -570,6 +657,17 @@ 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,
))
{
log::error!("Failed to update type filter value: {}", err);
}
// Request a module list update
if let Err(err) =
self.backend.send_command(BackendCommand::ListModules(
Expand All @@ -593,8 +691,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) =
Expand Down Expand Up @@ -653,22 +751,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 {
Expand Down Expand Up @@ -752,7 +878,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) => {
Expand All @@ -765,7 +896,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;
}
Expand Down Expand Up @@ -883,7 +1014,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
Expand All @@ -894,7 +1025,7 @@ impl ResymApp {
{
log::error!(
"Failed to list cross-references to type #0x{:x}: {err}",
type_index.0
type_index
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<I: Copy> {
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<I: Copy> IndexListComponent<I> {
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<CB: FnMut(&str, TypeIndex)>(
&mut self,
ui: &mut egui::Ui,
on_type_selected: &mut CB,
) {
let num_rows = self.filtered_type_list.len();
pub fn update<CB: FnMut(&str, I)>(&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(
Expand All @@ -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);
}
}
});
Expand All @@ -71,8 +66,8 @@ impl TypeListComponent {
}
}

impl Default for TypeListComponent {
impl<I: Copy> Default for IndexListComponent<I> {
fn default() -> Self {
Self::new(TypeListOrdering::None)
Self::new(IndexListOrdering::None)
}
}
Loading

0 comments on commit 5f7212e

Please sign in to comment.