use crate::state::{
Breakpoint,
DebugEval,
ProgramState,
};
use fuel_types::{
ContractId,
Word,
};
use hashbrown::{
HashMap,
HashSet,
};
#[derive(Debug, Default, Clone)]
pub struct Debugger {
is_active: bool,
single_stepping: bool,
breakpoints: HashMap<ContractId, HashSet<Word>>,
last_state: Option<ProgramState>,
}
impl Debugger {
pub const fn is_active(&self) -> bool {
self.is_active
}
pub const fn single_stepping(&self) -> bool {
self.single_stepping
}
pub fn set_single_stepping(&mut self, single_stepping: bool) {
self.is_active = true;
self.single_stepping = single_stepping;
}
pub fn clear_breakpoints(&mut self) {
self.breakpoints.clear();
}
pub fn set_breakpoint(&mut self, breakpoint: Breakpoint) {
self.is_active = true;
let contract = *breakpoint.contract();
let pc = breakpoint.pc();
self.breakpoints
.get_mut(&contract)
.map(|set| set.insert(pc))
.map(|_| ())
.unwrap_or_else(|| {
let mut set = HashSet::new();
set.insert(pc);
self.breakpoints.insert(contract, set);
});
}
pub fn remove_breakpoint(&mut self, breakpoint: &Breakpoint) {
self.is_active = true;
self.breakpoints
.get_mut(breakpoint.contract())
.map(|set| set.remove(&breakpoint.pc()));
}
pub fn eval_state(&mut self, contract: Option<&ContractId>, pc: Word) -> DebugEval {
let contract = contract.copied().unwrap_or_default();
let last_state = self.last_state.take();
let current = Breakpoint::raw(contract, pc);
if self.single_stepping {
return match last_state {
Some(s) if s == current => DebugEval::Continue,
_ => current.into(),
}
}
self.breakpoints
.get(&contract)
.and_then(|set| set.get(&pc))
.map(|_| match last_state {
Some(s) if s == current => DebugEval::Continue,
_ => current.into(),
})
.unwrap_or_default()
}
pub fn set_last_state(&mut self, state: ProgramState) {
self.is_active = true;
self.last_state.replace(state);
}
pub const fn last_state(&self) -> &Option<ProgramState> {
&self.last_state
}
}