use parser::{
FunctionCall, FunctionCallIndirectOrigin, FunctionCallKind, FunctionCallOrigin,
FunctionCallParameter, Unit,
};
use crate::Result;
use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter};
fn print_kind(kind: FunctionCallKind, w: &mut dyn ValuePrinter) -> Result<()> {
match kind {
FunctionCallKind::Normal => write!(w, "call"),
FunctionCallKind::Tail => write!(w, "tail call"),
}?;
Ok(())
}
fn print_address(f: &FunctionCall, w: &mut dyn ValuePrinter) -> Result<()> {
if let Some(address) = f.return_address() {
write!(w, "{:#x}", address)?;
}
Ok(())
}
fn print_header(f: &FunctionCall, w: &mut dyn ValuePrinter) -> Result<()> {
print_kind(f.kind(), w)?;
write!(w, " at ")?;
if let Some(addr) = f.call_address() {
write!(w, "{:#x}", addr)?;
} else if f.return_address().is_some() {
write!(w, "<before return>")?;
} else {
write!(w, "<unknown>")?;
}
print_origin(f.origin(), w)?;
Ok(())
}
fn print_origin(origin: Option<&FunctionCallOrigin>, w: &mut dyn ValuePrinter) -> Result<()> {
match origin {
Some(FunctionCallOrigin::Direct(f)) => {
write!(w, " -> ")?;
print::function::print_ref(f, w)?;
}
Some(FunctionCallOrigin::Indirect(indirect)) => {
write!(w, " -> ")?;
match indirect {
FunctionCallIndirectOrigin::Variable(v) => {
write!(w, "indirect(global:{})", v.name().unwrap_or("<unknown>"))?;
}
FunctionCallIndirectOrigin::LocalVariable(_) => {
write!(w, "indirect(local variable)")?;
}
FunctionCallIndirectOrigin::Parameter(_param_offset) => {
write!(w, "indirect(parameter)")?;
}
FunctionCallIndirectOrigin::Member(_member_offset) => {
write!(w, "indirect(member)")?;
}
}
}
None => {} }
Ok(())
}
impl<'input> Print for FunctionCall<'input> {
type Arg = Unit<'input>;
fn print(&self, state: &mut PrintState, unit: &Self::Arg) -> parser::Result<()> {
state.expanded(
|state| state.line(|w, _state| print_header(self, w)),
|state| {
state.field("return address", |w, _state| print_address(self, w))?;
if state.options().print_variable_locations {
state.field_expanded("target", |state| {
print::location::print_pieces(state, self.target())
})?;
}
if state.options().print_source {
state.field("source", |w, _state| {
print::source::print(self.source(), w, unit)
})?;
}
state.field_expanded("parameters", |state| {
for param in self.parameters() {
param.print(state, unit)?;
}
Ok(())
})?;
Ok(())
},
)?;
Ok(())
}
fn diff(
state: &mut super::DiffState,
arg_a: &Self::Arg,
a: &Self,
arg_b: &Self::Arg,
b: &Self,
) -> parser::Result<()> {
state.expanded(
|state| state.line(a, b, |w, _state, x| print_header(x, w)),
|state| {
state.field("return address", a, b, |w, _state, x| print_address(x, w))?;
if state.options().print_variable_locations {
state.field_expanded("target", |state| {
print::location::diff_pieces(state, a.target(), b.target())
})?;
}
if state.options().print_source {
state.field("source", (arg_a, a), (arg_b, b), |w, _state, (unit, x)| {
print::source::print(x.source(), w, unit)
})?;
}
if !a.parameters().is_empty() || !b.parameters().is_empty() {
state.field_expanded("parameters", |state| {
let params_a = a.parameters();
let params_b = b.parameters();
let max_len = params_a.len().max(params_b.len());
for i in 0..max_len {
match (params_a.get(i), params_b.get(i)) {
(Some(param_a), Some(param_b)) => {
FunctionCallParameter::diff(
state, arg_a, param_a, arg_b, param_b,
)?;
}
(Some(param_a), None) => {
state.prefix_delete(|state| param_a.print(state, arg_a))?;
}
(None, Some(param_b)) => {
state.prefix_add(|state| param_b.print(state, arg_b))?;
}
(None, None) => unreachable!(),
}
}
Ok(())
})?;
}
Ok(())
},
)?;
Ok(())
}
}
impl<'input> DiffList for FunctionCall<'input> {
fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize {
1
}
fn diff_cost(state: &DiffState, _unit_a: &Unit, a: &Self, _unit_b: &Unit, b: &Self) -> usize {
let mut cost = 0;
if a.kind().cmp(&b.kind()) != std::cmp::Ordering::Equal {
cost += 1;
}
if a.call_address().cmp(&b.call_address()) != std::cmp::Ordering::Equal {
cost += 1;
}
if a.return_address().cmp(&b.return_address()) != std::cmp::Ordering::Equal {
cost += 1;
}
match (a.origin(), b.origin()) {
(Some(origin_a), Some(origin_b)) => {
if std::mem::discriminant(origin_a) != std::mem::discriminant(origin_b) {
cost += 2;
} else {
match (origin_a, origin_b) {
(FunctionCallOrigin::Direct(f_a), FunctionCallOrigin::Direct(f_b)) => {
if parser::Function::cmp_id(state.hash_a(), f_a, state.hash_b(), f_b)
!= std::cmp::Ordering::Equal
{
cost += 1;
}
}
(
FunctionCallOrigin::Indirect(ind_a),
FunctionCallOrigin::Indirect(ind_b),
) => {
if std::mem::discriminant(ind_a) != std::mem::discriminant(ind_b) {
cost += 1;
}
}
_ => {}
}
}
}
(None, None) => {}
_ => {
cost += 1;
}
}
cost
}
}
fn print_parameter_header(
param: &FunctionCallParameter,
w: &mut dyn ValuePrinter,
hash: &parser::FileHash,
) -> Result<()> {
if let Some(name) = param.name() {
write!(w, "{}", name)?;
} else {
write!(w, "<unnamed>")?;
}
if let Some(ty) = param.ty(hash) {
write!(w, ": ")?;
print::types::print_ref(Some(ty), w, hash)?;
}
Ok(())
}
impl<'input> Print for FunctionCallParameter<'input> {
type Arg = Unit<'input>;
fn print(&self, state: &mut PrintState, _unit: &Self::Arg) -> Result<()> {
state.expanded(
|state| state.line(|w, hash| print_parameter_header(self, w, hash)),
|state| {
if state.options().print_variable_locations {
state.field_expanded("location", |state| {
print::location::print_pieces(state, self.location())
})?;
state.field_expanded("value", |state| {
print::location::print_pieces(state, self.value())
})?;
state.field_expanded("data_location", |state| {
print::location::print_pieces(state, self.data_location())
})?;
state.field_expanded("data_value_location", |state| {
print::location::print_pieces(state, self.data_value())
})?;
}
Ok(())
},
)
}
fn diff(
state: &mut print::DiffState,
_unit_a: &Self::Arg,
a: &Self,
_unit_b: &Self::Arg,
b: &Self,
) -> Result<()> {
state.expanded(
|state| {
state.line(a, b, |w, hash, param| {
print_parameter_header(param, w, hash)
})
},
|state| {
if state.options().print_variable_locations {
state.field_expanded("location", |state| {
print::location::diff_pieces(state, a.location(), b.location())
})?;
state.field_expanded("value", |state| {
print::location::diff_pieces(state, a.value(), b.value())
})?;
state.field_expanded("data_location", |state| {
print::location::diff_pieces(state, a.data_location(), b.data_location())
})?;
state.field_expanded("data_value_location", |state| {
print::location::diff_pieces(state, a.data_value(), b.data_value())
})?;
}
Ok(())
},
)
}
}