use crate::debugger::VMDebugger;
pub trait DebuggerIntegration {
fn trace_state(&self);
fn debug_break(&self);
fn instruction_pointer(&self) -> usize;
fn stack_size(&self) -> usize;
fn stack_top(&self) -> Option<String>;
fn stack_values_vec(&self) -> Vec<String>;
fn call_stack_depth(&self) -> usize;
fn call_frames(&self) -> &[super::CallFrame];
fn local_values_vec(&self) -> Vec<String>;
fn module_binding_values(&self) -> Vec<(u64, shape_value::NativeKind)>;
fn set_module_binding(&mut self, index: usize, bits: u64, kind: shape_value::NativeKind);
fn set_trace_mode(&mut self, enabled: bool);
fn debugger_mut(&mut self) -> Option<&mut VMDebugger>;
fn has_debugger(&self) -> bool;
}
impl DebuggerIntegration for super::VirtualMachine {
fn trace_state(&self) {
let stack_strs: Vec<String> = (0..self.sp)
.map(|i| {
let (bits, kind) = self.stack_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect();
println!("IP: {}, Stack: {:?}", self.ip, stack_strs);
if self.ip < self.program.instructions.len() {
println!("Next: {:?}", self.program.instructions[self.ip]);
}
}
fn debug_break(&self) {
println!("=== DEBUG BREAK ===");
println!("IP: {}, SP: {}", self.ip, self.sp);
let stack_strs: Vec<String> = (0..self.sp)
.map(|i| {
let (bits, kind) = self.stack_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect();
println!("Stack: {:?}", stack_strs);
if let Some(frame) = self.call_stack.last() {
let bp = frame.base_pointer;
let end = (bp + frame.locals_count).min(self.sp);
let locals: Vec<String> = (bp..end)
.map(|i| {
let (bits, kind) = self.stack_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect();
println!("Locals (bp={}): {:?}", bp, locals);
}
let module_bindings_kinded: Vec<String> = (0..self.module_bindings_len())
.map(|i| {
let (bits, kind) = self.module_binding_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect();
println!("Globals: {:?}", module_bindings_kinded);
println!("Call stack depth: {}", self.call_stack.len());
}
fn instruction_pointer(&self) -> usize {
self.ip
}
fn stack_size(&self) -> usize {
self.sp
}
fn stack_top(&self) -> Option<String> {
if self.sp > 0 {
let (bits, kind) = self.stack_read_kinded_raw(self.sp - 1);
Some(format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind))
} else {
None
}
}
fn stack_values_vec(&self) -> Vec<String> {
(0..self.sp)
.map(|i| {
let (bits, kind) = self.stack_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect()
}
fn call_stack_depth(&self) -> usize {
self.call_stack.len()
}
fn call_frames(&self) -> &[super::CallFrame] {
&self.call_stack
}
fn local_values_vec(&self) -> Vec<String> {
if let Some(frame) = self.call_stack.last() {
let bp = frame.base_pointer;
let end = (bp + frame.locals_count).min(self.sp);
(bp..end)
.map(|i| {
let (bits, kind) = self.stack_read_kinded_raw(i);
format!("{{ bits: {:#x}, kind: {:?} }}", bits, kind)
})
.collect()
} else {
vec![]
}
}
fn module_binding_values(&self) -> Vec<(u64, shape_value::NativeKind)> {
let len = self.module_bindings_len();
(0..len)
.map(|i| self.module_binding_read_kinded_raw(i))
.collect()
}
fn set_module_binding(&mut self, index: usize, bits: u64, kind: shape_value::NativeKind) {
self.module_binding_write_kinded(index, bits, kind);
}
fn set_trace_mode(&mut self, enabled: bool) {
self.config.trace_execution = enabled;
if let Some(ref mut debugger) = self.debugger {
debugger.set_trace_mode(enabled);
}
}
fn debugger_mut(&mut self) -> Option<&mut VMDebugger> {
self.debugger.as_mut()
}
fn has_debugger(&self) -> bool {
self.debugger.is_some()
}
}