Skip to main content

shape_vm/executor/
debugger_integration.rs

1//! Debugger integration for the VM
2//!
3//! This module handles debugger support, tracing, and debugging operations
4//! for the virtual machine.
5//!
6//! Display-only methods (stack_top, stack_values_vec, local_values_vec)
7//! return `ExternalValue` for safe serialization and display.
8//! Data-flow methods (module_binding_values, set_global) retain `ValueWord` since
9//! they feed values back into the VM.
10
11use crate::debugger::VMDebugger;
12use shape_value::{ExternalValue, ValueWord};
13
14/// Debugger integration for VirtualMachine
15pub trait DebuggerIntegration {
16    /// Trace VM state (for debugging)
17    fn trace_state(&self);
18
19    /// Trigger a debug break
20    fn debug_break(&self);
21
22    /// Get current instruction pointer
23    fn instruction_pointer(&self) -> usize;
24
25    /// Get stack size
26    fn stack_size(&self) -> usize;
27
28    /// Get top of stack as ExternalValue (display only)
29    fn stack_top(&self) -> Option<ExternalValue>;
30
31    /// Get all stack values as ExternalValues (display only)
32    fn stack_values_vec(&self) -> Vec<ExternalValue>;
33
34    /// Get call stack depth
35    fn call_stack_depth(&self) -> usize;
36
37    /// Get call frames (for debugging)
38    fn call_frames(&self) -> &[super::CallFrame];
39
40    /// Get local variables as ExternalValues (display only)
41    fn local_values_vec(&self) -> Vec<ExternalValue>;
42
43    /// Get module_binding variables (data-flow: values are stored back into VM)
44    fn module_binding_values(&self) -> Vec<ValueWord>;
45
46    /// Set a module_binding variable by index
47    fn set_module_binding(&mut self, index: usize, value: ValueWord);
48
49    /// Set trace mode
50    fn set_trace_mode(&mut self, enabled: bool);
51
52    /// Get mutable reference to debugger
53    fn debugger_mut(&mut self) -> Option<&mut VMDebugger>;
54
55    /// Check if debugger is enabled
56    fn has_debugger(&self) -> bool;
57}
58
59impl DebuggerIntegration for super::VirtualMachine {
60    fn trace_state(&self) {
61        let schemas = &self.program.type_schema_registry;
62        let stack_vals: Vec<_> = self.stack[..self.sp]
63            .iter()
64            .map(|nb| shape_value::nb_to_external(nb, schemas))
65            .collect();
66        println!("IP: {}, Stack: {:?}", self.ip, stack_vals);
67        if self.ip < self.program.instructions.len() {
68            println!("Next: {:?}", self.program.instructions[self.ip]);
69        }
70    }
71
72    fn debug_break(&self) {
73        let schemas = &self.program.type_schema_registry;
74        println!("=== DEBUG BREAK ===");
75        println!("IP: {}, SP: {}", self.ip, self.sp);
76        let stack_vals: Vec<_> = self.stack[..self.sp]
77            .iter()
78            .map(|nb| shape_value::nb_to_external(nb, schemas))
79            .collect();
80        println!("Stack: {:?}", stack_vals);
81        if let Some(frame) = self.call_stack.last() {
82            let bp = frame.base_pointer;
83            let end = (bp + frame.locals_count).min(self.sp);
84            let locals: Vec<_> = self.stack[bp..end]
85                .iter()
86                .map(|nb| shape_value::nb_to_external(nb, schemas))
87                .collect();
88            println!("Locals (bp={}): {:?}", bp, locals);
89        }
90        let module_bindings: Vec<_> = self
91            .module_bindings
92            .iter()
93            .map(|nb| shape_value::nb_to_external(nb, schemas))
94            .collect();
95        println!("Globals: {:?}", module_bindings);
96        println!("Call stack depth: {}", self.call_stack.len());
97    }
98
99    // ===== Debugger Interface Methods =====
100
101    fn instruction_pointer(&self) -> usize {
102        self.ip
103    }
104
105    fn stack_size(&self) -> usize {
106        self.sp
107    }
108
109    fn stack_top(&self) -> Option<ExternalValue> {
110        if self.sp > 0 {
111            let schemas = &self.program.type_schema_registry;
112            Some(shape_value::nb_to_external(
113                &self.stack[self.sp - 1],
114                schemas,
115            ))
116        } else {
117            None
118        }
119    }
120
121    fn stack_values_vec(&self) -> Vec<ExternalValue> {
122        let schemas = &self.program.type_schema_registry;
123        self.stack[..self.sp]
124            .iter()
125            .map(|nb| shape_value::nb_to_external(nb, schemas))
126            .collect()
127    }
128
129    fn call_stack_depth(&self) -> usize {
130        self.call_stack.len()
131    }
132
133    fn call_frames(&self) -> &[super::CallFrame] {
134        &self.call_stack
135    }
136
137    fn local_values_vec(&self) -> Vec<ExternalValue> {
138        let schemas = &self.program.type_schema_registry;
139        if let Some(frame) = self.call_stack.last() {
140            let bp = frame.base_pointer;
141            let end = (bp + frame.locals_count).min(self.sp);
142            self.stack[bp..end]
143                .iter()
144                .map(|nb| shape_value::nb_to_external(nb, schemas))
145                .collect()
146        } else {
147            vec![]
148        }
149    }
150
151    fn module_binding_values(&self) -> Vec<ValueWord> {
152        self.module_bindings.iter().map(|nb| nb.clone()).collect()
153    }
154
155    fn set_module_binding(&mut self, index: usize, value: ValueWord) {
156        use shape_value::ValueWord;
157        if index < self.module_bindings.len() {
158            // BARRIER: heap write site — debugger overwrites module binding slot
159            self.module_bindings[index] = value;
160        } else {
161            self.module_bindings.resize_with(index + 1, ValueWord::none);
162            // BARRIER: heap write site — debugger overwrites module binding slot (after resize)
163            self.module_bindings[index] = value;
164        }
165    }
166
167    fn set_trace_mode(&mut self, enabled: bool) {
168        self.config.trace_execution = enabled;
169        if let Some(ref mut debugger) = self.debugger {
170            debugger.set_trace_mode(enabled);
171        }
172    }
173
174    fn debugger_mut(&mut self) -> Option<&mut VMDebugger> {
175        self.debugger.as_mut()
176    }
177
178    fn has_debugger(&self) -> bool {
179        self.debugger.is_some()
180    }
181}