mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-11 14:41:51 +00:00
Restructure diffing code & initial 3-way diffing (WIP)
This commit is contained in:
@@ -364,12 +364,12 @@ impl App {
|
||||
}
|
||||
|
||||
if let Some(result) = &diff_state.build {
|
||||
if let Some(obj) = &result.first_obj {
|
||||
if let Some((obj, _)) = &result.first_obj {
|
||||
if file_modified(&obj.path, obj.timestamp) {
|
||||
config.queue_reload = true;
|
||||
}
|
||||
}
|
||||
if let Some(obj) = &result.second_obj {
|
||||
if let Some((obj, _)) = &result.second_obj {
|
||||
if file_modified(&obj.path, obj.timestamp) {
|
||||
config.queue_reload = true;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use objdiff_core::{
|
||||
diff::{diff_objs, DiffObjConfig},
|
||||
diff::{diff_objs, DiffObjConfig, ObjDiff},
|
||||
obj::{read, ObjInfo},
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
@@ -75,8 +75,8 @@ impl ObjDiffConfig {
|
||||
pub struct ObjDiffResult {
|
||||
pub first_status: BuildStatus,
|
||||
pub second_status: BuildStatus,
|
||||
pub first_obj: Option<ObjInfo>,
|
||||
pub second_obj: Option<ObjInfo>,
|
||||
pub first_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub second_obj: Option<(ObjInfo, ObjDiff)>,
|
||||
pub time: OffsetDateTime,
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ fn run_build(
|
||||
|
||||
let time = OffsetDateTime::now_utc();
|
||||
|
||||
let mut first_obj =
|
||||
let first_obj =
|
||||
match &obj_config.target_path {
|
||||
Some(target_path) if first_status.success => {
|
||||
update_status(
|
||||
@@ -231,7 +231,7 @@ fn run_build(
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut second_obj = match &obj_config.base_path {
|
||||
let second_obj = match &obj_config.base_path {
|
||||
Some(base_path) if second_status.success => {
|
||||
update_status(
|
||||
context,
|
||||
@@ -249,10 +249,16 @@ fn run_build(
|
||||
};
|
||||
|
||||
update_status(context, "Performing diff".to_string(), 4, total, &cancel)?;
|
||||
diff_objs(&config.diff_obj_config, first_obj.as_mut(), second_obj.as_mut())?;
|
||||
let result = diff_objs(&config.diff_obj_config, first_obj.as_ref(), second_obj.as_ref(), None)?;
|
||||
|
||||
update_status(context, "Complete".to_string(), total, total, &cancel)?;
|
||||
Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time }))
|
||||
Ok(Box::new(ObjDiffResult {
|
||||
first_status,
|
||||
second_status,
|
||||
first_obj: first_obj.and_then(|o| result.left.map(|d| (o, d))),
|
||||
second_obj: second_obj.and_then(|o| result.right.map(|d| (o, d))),
|
||||
time,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn start_build(ctx: &egui::Context, config: ObjDiffConfig) -> JobState {
|
||||
|
||||
@@ -2,19 +2,22 @@ use std::{cmp::min, default::Default, mem::take};
|
||||
|
||||
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
use objdiff_core::obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection};
|
||||
use objdiff_core::{
|
||||
diff::{ObjDataDiff, ObjDataDiffKind, ObjDiff},
|
||||
obj::ObjInfo,
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{DiffViewState, SymbolReference, View},
|
||||
symbol_diff::{DiffViewState, SymbolRefByName, View},
|
||||
write_text,
|
||||
};
|
||||
|
||||
const BYTES_PER_ROW: usize = 16;
|
||||
|
||||
fn find_section<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSection> {
|
||||
obj.sections.iter().find(|section| section.name == selected_symbol.section_name)
|
||||
fn find_section(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<usize> {
|
||||
obj.sections.iter().position(|section| section.name == selected_symbol.section_name)
|
||||
}
|
||||
|
||||
fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) {
|
||||
@@ -130,16 +133,21 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
|
||||
|
||||
fn data_table_ui(
|
||||
table: TableBuilder<'_>,
|
||||
left_obj: Option<&ObjInfo>,
|
||||
right_obj: Option<&ObjInfo>,
|
||||
selected_symbol: &SymbolReference,
|
||||
left_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||
right_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||
selected_symbol: &SymbolRefByName,
|
||||
config: &Appearance,
|
||||
) -> Option<()> {
|
||||
let left_section = left_obj.and_then(|obj| find_section(obj, selected_symbol));
|
||||
let right_section = right_obj.and_then(|obj| find_section(obj, selected_symbol));
|
||||
let left_section = left_obj.and_then(|(obj, diff)| {
|
||||
find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i]))
|
||||
});
|
||||
let right_section = right_obj.and_then(|(obj, diff)| {
|
||||
find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i]))
|
||||
});
|
||||
|
||||
let total_bytes = left_section
|
||||
.or(right_section)?
|
||||
.1
|
||||
.data_diff
|
||||
.iter()
|
||||
.fold(0usize, |accum, item| accum + item.len);
|
||||
@@ -148,8 +156,8 @@ fn data_table_ui(
|
||||
}
|
||||
let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
|
||||
|
||||
let left_diffs = left_section.map(|section| split_diffs(§ion.data_diff));
|
||||
let right_diffs = right_section.map(|section| split_diffs(§ion.data_diff));
|
||||
let left_diffs = left_section.map(|(_, section)| split_diffs(§ion.data_diff));
|
||||
let right_diffs = right_section.map(|(_, section)| split_diffs(§ion.data_diff));
|
||||
|
||||
table.body(|body| {
|
||||
body.rows(config.code_font.size, total_rows, |mut row| {
|
||||
|
||||
@@ -4,17 +4,17 @@ use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
|
||||
use egui_extras::{Column, TableBuilder, TableRow};
|
||||
use objdiff_core::{
|
||||
arch::ObjArch,
|
||||
diff::display::{display_diff, DiffText, HighlightKind},
|
||||
obj::{
|
||||
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection,
|
||||
ObjSymbol,
|
||||
diff::{
|
||||
display::{display_diff, DiffText, HighlightKind},
|
||||
get_symbol, ObjDiff, ObjInsDiff, ObjInsDiffKind, SymbolRef,
|
||||
},
|
||||
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol},
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
|
||||
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolRefByName, View},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -126,19 +126,15 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
|
||||
});
|
||||
}
|
||||
|
||||
fn find_symbol<'a>(
|
||||
obj: &'a ObjInfo,
|
||||
selected_symbol: &SymbolReference,
|
||||
) -> Option<(&'a dyn ObjArch, &'a ObjSection, &'a ObjSymbol)> {
|
||||
obj.sections.iter().find_map(|section| {
|
||||
section.symbols.iter().find_map(|symbol| {
|
||||
(symbol.name == selected_symbol.symbol_name).then_some((
|
||||
obj.arch.as_ref(),
|
||||
section,
|
||||
symbol,
|
||||
))
|
||||
})
|
||||
})
|
||||
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||
if symbol.name == selected_symbol.symbol_name {
|
||||
return Some(SymbolRef { section_idx, symbol_idx });
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn diff_text_ui(
|
||||
@@ -248,18 +244,21 @@ fn asm_row_ui(
|
||||
|
||||
fn asm_col_ui(
|
||||
row: &mut TableRow<'_, '_>,
|
||||
ins_diff: &ObjInsDiff,
|
||||
arch: &dyn ObjArch,
|
||||
section: &ObjSection,
|
||||
symbol: &ObjSymbol,
|
||||
obj: &(ObjInfo, ObjDiff),
|
||||
symbol_ref: SymbolRef,
|
||||
appearance: &Appearance,
|
||||
ins_view_state: &mut FunctionViewState,
|
||||
) {
|
||||
let (section, symbol) = get_symbol(&obj.0, symbol_ref);
|
||||
let ins_diff = &obj.1.sections[symbol_ref.section_idx].symbols[symbol_ref.symbol_idx]
|
||||
.instructions[row.index()];
|
||||
let (_, response) = row.col(|ui| {
|
||||
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
|
||||
});
|
||||
if let Some(ins) = &ins_diff.ins {
|
||||
response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, arch, section, ins, appearance));
|
||||
response.on_hover_ui_at_pointer(|ui| {
|
||||
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, appearance)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,41 +270,40 @@ fn empty_col_ui(row: &mut TableRow<'_, '_>) {
|
||||
|
||||
fn asm_table_ui(
|
||||
table: TableBuilder<'_>,
|
||||
left_obj: Option<&ObjInfo>,
|
||||
right_obj: Option<&ObjInfo>,
|
||||
selected_symbol: &SymbolReference,
|
||||
left_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||
right_obj: Option<&(ObjInfo, ObjDiff)>,
|
||||
selected_symbol: &SymbolRefByName,
|
||||
appearance: &Appearance,
|
||||
ins_view_state: &mut FunctionViewState,
|
||||
) -> Option<()> {
|
||||
let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol));
|
||||
let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol));
|
||||
let instructions_len = left_symbol.or(right_symbol).map(|(_, _, s)| s.instructions.len())?;
|
||||
let left_symbol = left_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol));
|
||||
let right_symbol = right_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol));
|
||||
let instructions_len = match (left_symbol, right_symbol) {
|
||||
(Some(left_symbol_ref), Some(_)) => left_obj.unwrap().1.sections
|
||||
[left_symbol_ref.section_idx]
|
||||
.symbols[left_symbol_ref.symbol_idx]
|
||||
.instructions
|
||||
.len(),
|
||||
(Some(left_symbol_ref), None) => left_obj.unwrap().1.sections[left_symbol_ref.section_idx]
|
||||
.symbols[left_symbol_ref.symbol_idx]
|
||||
.instructions
|
||||
.len(),
|
||||
(None, Some(right_symbol_ref)) => right_obj.unwrap().1.sections
|
||||
[right_symbol_ref.section_idx]
|
||||
.symbols[right_symbol_ref.symbol_idx]
|
||||
.instructions
|
||||
.len(),
|
||||
(None, None) => return None,
|
||||
};
|
||||
table.body(|body| {
|
||||
body.rows(appearance.code_font.size, instructions_len, |mut row| {
|
||||
let row_index = row.index();
|
||||
if let Some((arch, section, symbol)) = left_symbol {
|
||||
asm_col_ui(
|
||||
&mut row,
|
||||
&symbol.instructions[row_index],
|
||||
arch,
|
||||
section,
|
||||
symbol,
|
||||
appearance,
|
||||
ins_view_state,
|
||||
);
|
||||
if let (Some(left_obj), Some(left_symbol_ref)) = (left_obj, left_symbol) {
|
||||
asm_col_ui(&mut row, left_obj, left_symbol_ref, appearance, ins_view_state);
|
||||
} else {
|
||||
empty_col_ui(&mut row);
|
||||
}
|
||||
if let Some((arch, section, symbol)) = right_symbol {
|
||||
asm_col_ui(
|
||||
&mut row,
|
||||
&symbol.instructions[row_index],
|
||||
arch,
|
||||
section,
|
||||
symbol,
|
||||
appearance,
|
||||
ins_view_state,
|
||||
);
|
||||
if let (Some(right_obj), Some(right_symbol_ref)) = (right_obj, right_symbol) {
|
||||
asm_col_ui(&mut row, right_obj, right_symbol_ref, appearance, ins_view_state);
|
||||
} else {
|
||||
empty_col_ui(&mut row);
|
||||
}
|
||||
@@ -412,8 +410,12 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
|
||||
if let Some(match_percent) = result
|
||||
.second_obj
|
||||
.as_ref()
|
||||
.and_then(|obj| find_symbol(obj, selected_symbol))
|
||||
.and_then(|(_, _, symbol)| symbol.match_percent)
|
||||
.and_then(|(obj, diff)| {
|
||||
find_symbol(obj, selected_symbol).map(|sref| {
|
||||
&diff.sections[sref.section_idx].symbols[sref.symbol_idx]
|
||||
})
|
||||
})
|
||||
.and_then(|symbol| symbol.match_percent)
|
||||
{
|
||||
ui.colored_label(
|
||||
match_color_for_symbol(match_percent, appearance),
|
||||
|
||||
@@ -5,7 +5,10 @@ use egui::{
|
||||
SelectableLabel, TextEdit, Ui, Vec2, Widget,
|
||||
};
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
use objdiff_core::obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags};
|
||||
use objdiff_core::{
|
||||
diff::{ObjDiff, ObjSymbolDiff},
|
||||
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::AppConfigRef,
|
||||
@@ -17,7 +20,7 @@ use crate::{
|
||||
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
||||
};
|
||||
|
||||
pub struct SymbolReference {
|
||||
pub struct SymbolRefByName {
|
||||
pub symbol_name: String,
|
||||
pub demangled_symbol_name: Option<String>,
|
||||
pub section_name: String,
|
||||
@@ -50,7 +53,7 @@ pub struct DiffViewState {
|
||||
#[derive(Default)]
|
||||
pub struct SymbolViewState {
|
||||
pub highlighted_symbol: Option<String>,
|
||||
pub selected_symbol: Option<SymbolReference>,
|
||||
pub selected_symbol: Option<SymbolRefByName>,
|
||||
pub reverse_fn_order: bool,
|
||||
pub disable_reverse_fn_order: bool,
|
||||
pub show_hidden_symbols: bool,
|
||||
@@ -180,6 +183,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
|
||||
fn symbol_ui(
|
||||
ui: &mut Ui,
|
||||
symbol: &ObjSymbol,
|
||||
symbol_diff: &ObjSymbolDiff,
|
||||
section: Option<&ObjSection>,
|
||||
state: &mut SymbolViewState,
|
||||
appearance: &Appearance,
|
||||
@@ -210,7 +214,7 @@ fn symbol_ui(
|
||||
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
|
||||
}
|
||||
write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
if let Some(match_percent) = symbol.match_percent {
|
||||
if let Some(match_percent) = symbol_diff.match_percent {
|
||||
write_text("(", appearance.text_color, &mut job, appearance.code_font.clone());
|
||||
write_text(
|
||||
&format!("{match_percent:.0}%"),
|
||||
@@ -228,14 +232,14 @@ fn symbol_ui(
|
||||
if response.clicked() {
|
||||
if let Some(section) = section {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
state.selected_symbol = Some(SymbolReference {
|
||||
state.selected_symbol = Some(SymbolRefByName {
|
||||
symbol_name: symbol.name.clone(),
|
||||
demangled_symbol_name: symbol.demangled_name.clone(),
|
||||
section_name: section.name.clone(),
|
||||
});
|
||||
ret = Some(View::FunctionDiff);
|
||||
} else if section.kind == ObjSectionKind::Data {
|
||||
state.selected_symbol = Some(SymbolReference {
|
||||
state.selected_symbol = Some(SymbolRefByName {
|
||||
symbol_name: section.name.clone(),
|
||||
demangled_symbol_name: None,
|
||||
section_name: section.name.clone(),
|
||||
@@ -262,7 +266,7 @@ fn symbol_matches_search(symbol: &ObjSymbol, search_str: &str) -> bool {
|
||||
#[must_use]
|
||||
fn symbol_list_ui(
|
||||
ui: &mut Ui,
|
||||
obj: &ObjInfo,
|
||||
obj: &(ObjInfo, ObjDiff),
|
||||
state: &mut SymbolViewState,
|
||||
lower_search: &str,
|
||||
appearance: &Appearance,
|
||||
@@ -273,34 +277,50 @@ fn symbol_list_ui(
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap = Some(false);
|
||||
|
||||
if !obj.common.is_empty() {
|
||||
if !obj.0.common.is_empty() {
|
||||
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
|
||||
for symbol in &obj.common {
|
||||
ret = ret.or(symbol_ui(ui, symbol, None, state, appearance));
|
||||
for (symbol, symbol_diff) in obj.0.common.iter().zip(&obj.1.common) {
|
||||
ret = ret.or(symbol_ui(ui, symbol, symbol_diff, None, state, appearance));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for section in &obj.sections {
|
||||
for (section, section_diff) in obj.0.sections.iter().zip(&obj.1.sections) {
|
||||
CollapsingHeader::new(format!("{} ({:x})", section.name, section.size))
|
||||
.id_source(Id::new(section.name.clone()).with(section.index))
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
|
||||
for symbol in section.symbols.iter().rev() {
|
||||
for (symbol, symbol_diff) in
|
||||
section.symbols.iter().zip(§ion_diff.symbols).rev()
|
||||
{
|
||||
if !symbol_matches_search(symbol, lower_search) {
|
||||
continue;
|
||||
}
|
||||
ret =
|
||||
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
||||
ret = ret.or(symbol_ui(
|
||||
ui,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
Some(section),
|
||||
state,
|
||||
appearance,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
for symbol in §ion.symbols {
|
||||
for (symbol, symbol_diff) in
|
||||
section.symbols.iter().zip(§ion_diff.symbols)
|
||||
{
|
||||
if !symbol_matches_search(symbol, lower_search) {
|
||||
continue;
|
||||
}
|
||||
ret =
|
||||
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
||||
ret = ret.or(symbol_ui(
|
||||
ui,
|
||||
symbol,
|
||||
symbol_diff,
|
||||
Some(section),
|
||||
state,
|
||||
appearance,
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user