nu_protocol/debugger/
debugger_trait.rs

1//! Traits related to debugging
2//!
3//! The purpose of DebugContext is achieving static dispatch on `eval_xxx()` calls.
4//! The main Debugger trait is intended to be used as a trait object.
5//!
6//! The debugging information is stored in `EngineState` as the `debugger` field storing a `Debugger`
7//! trait object behind `Arc` and `Mutex`. To evaluate something (e.g., a block), first create a
8//! `Debugger` trait object (such as the `Profiler`). Then, add it to engine state via
9//! `engine_state.activate_debugger()`. This sets the internal state of EngineState to the debugging
10//! mode and calls `Debugger::activate()`. Now, you can call `eval_xxx::<WithDebug>()`. When you're
11//! done, call `engine_state.deactivate_debugger()` which calls `Debugger::deactivate()`, sets the
12//! EngineState into non-debugging mode, and returns the original mutated `Debugger` trait object.
13//! (`NoopDebugger` is placed in its place inside `EngineState`.) After deactivating, you can call
14//! `Debugger::report()` to get some output from the debugger, if necessary.
15
16use crate::{
17    PipelineData, ShellError, Span, Value,
18    ast::{Block, PipelineElement},
19    engine::EngineState,
20    ir::IrBlock,
21};
22use std::{fmt::Debug, ops::DerefMut};
23
24/// Trait used for static dispatch of `eval_xxx()` evaluator calls
25///
26/// DebugContext implements the same interface as Debugger (except activate() and deactivate(). It
27/// is intended to be implemented only by two structs
28/// * WithDebug which calls down to the Debugger methods
29/// * WithoutDebug with default implementation, i.e., empty calls to be optimized away
30pub trait DebugContext: Clone + Copy + Debug {
31    /// Called when the evaluator enters a block
32    #[allow(unused_variables)]
33    fn enter_block(engine_state: &EngineState, block: &Block) {}
34
35    /// Called when the evaluator leaves a block
36    #[allow(unused_variables)]
37    fn leave_block(engine_state: &EngineState, block: &Block) {}
38
39    /// Called when the AST evaluator enters a pipeline element
40    #[allow(unused_variables)]
41    fn enter_element(engine_state: &EngineState, element: &PipelineElement) {}
42
43    /// Called when the AST evaluator leaves a pipeline element
44    #[allow(unused_variables)]
45    fn leave_element(
46        engine_state: &EngineState,
47        element: &PipelineElement,
48        result: &Result<PipelineData, ShellError>,
49    ) {
50    }
51
52    /// Called before the IR evaluator runs an instruction
53    #[allow(unused_variables)]
54    fn enter_instruction(
55        engine_state: &EngineState,
56        ir_block: &IrBlock,
57        instruction_index: usize,
58        registers: &[PipelineData],
59    ) {
60    }
61
62    /// Called after the IR evaluator runs an instruction
63    #[allow(unused_variables)]
64    fn leave_instruction(
65        engine_state: &EngineState,
66        ir_block: &IrBlock,
67        instruction_index: usize,
68        registers: &[PipelineData],
69        error: Option<&ShellError>,
70    ) {
71    }
72}
73
74/// Marker struct signalizing that evaluation should use a Debugger
75///
76/// Trait methods call to Debugger trait object inside the supplied EngineState.
77#[derive(Clone, Copy, Debug)]
78pub struct WithDebug;
79
80impl DebugContext for WithDebug {
81    fn enter_block(engine_state: &EngineState, block: &Block) {
82        if let Ok(mut debugger) = engine_state.debugger.lock() {
83            debugger.deref_mut().enter_block(engine_state, block);
84        }
85    }
86
87    fn leave_block(engine_state: &EngineState, block: &Block) {
88        if let Ok(mut debugger) = engine_state.debugger.lock() {
89            debugger.deref_mut().leave_block(engine_state, block);
90        }
91    }
92
93    fn enter_element(engine_state: &EngineState, element: &PipelineElement) {
94        if let Ok(mut debugger) = engine_state.debugger.lock() {
95            debugger.deref_mut().enter_element(engine_state, element);
96        }
97    }
98
99    fn leave_element(
100        engine_state: &EngineState,
101        element: &PipelineElement,
102        result: &Result<PipelineData, ShellError>,
103    ) {
104        if let Ok(mut debugger) = engine_state.debugger.lock() {
105            debugger
106                .deref_mut()
107                .leave_element(engine_state, element, result);
108        }
109    }
110
111    fn enter_instruction(
112        engine_state: &EngineState,
113        ir_block: &IrBlock,
114        instruction_index: usize,
115        registers: &[PipelineData],
116    ) {
117        if let Ok(mut debugger) = engine_state.debugger.lock() {
118            debugger.deref_mut().enter_instruction(
119                engine_state,
120                ir_block,
121                instruction_index,
122                registers,
123            )
124        }
125    }
126
127    fn leave_instruction(
128        engine_state: &EngineState,
129        ir_block: &IrBlock,
130        instruction_index: usize,
131        registers: &[PipelineData],
132        error: Option<&ShellError>,
133    ) {
134        if let Ok(mut debugger) = engine_state.debugger.lock() {
135            debugger.deref_mut().leave_instruction(
136                engine_state,
137                ir_block,
138                instruction_index,
139                registers,
140                error,
141            )
142        }
143    }
144}
145
146/// Marker struct signalizing that evaluation should NOT use a Debugger
147///
148/// Trait methods are empty calls to be optimized away.
149#[derive(Clone, Copy, Debug)]
150pub struct WithoutDebug;
151
152impl DebugContext for WithoutDebug {}
153
154/// Debugger trait that every debugger needs to implement.
155///
156/// By default, its methods are empty. Not every Debugger needs to implement all of them.
157pub trait Debugger: Send + Debug {
158    /// Called by EngineState::activate_debugger().
159    ///
160    /// Intended for initializing the debugger.
161    fn activate(&mut self) {}
162
163    /// Called by EngineState::deactivate_debugger().
164    ///
165    /// Intended for wrapping up the debugger after a debugging session before returning back to
166    /// normal evaluation without debugging.
167    fn deactivate(&mut self) {}
168
169    /// Called when the evaluator enters a block
170    #[allow(unused_variables)]
171    fn enter_block(&mut self, engine_state: &EngineState, block: &Block) {}
172
173    /// Called when the evaluator leaves a block
174    #[allow(unused_variables)]
175    fn leave_block(&mut self, engine_state: &EngineState, block: &Block) {}
176
177    /// Called when the AST evaluator enters a pipeline element
178    #[allow(unused_variables)]
179    fn enter_element(&mut self, engine_state: &EngineState, pipeline_element: &PipelineElement) {}
180
181    /// Called when the AST evaluator leaves a pipeline element
182    #[allow(unused_variables)]
183    fn leave_element(
184        &mut self,
185        engine_state: &EngineState,
186        element: &PipelineElement,
187        result: &Result<PipelineData, ShellError>,
188    ) {
189    }
190
191    /// Called before the IR evaluator runs an instruction
192    #[allow(unused_variables)]
193    fn enter_instruction(
194        &mut self,
195        engine_state: &EngineState,
196        ir_block: &IrBlock,
197        instruction_index: usize,
198        registers: &[PipelineData],
199    ) {
200    }
201
202    /// Called after the IR evaluator runs an instruction
203    #[allow(unused_variables)]
204    fn leave_instruction(
205        &mut self,
206        engine_state: &EngineState,
207        ir_block: &IrBlock,
208        instruction_index: usize,
209        registers: &[PipelineData],
210        error: Option<&ShellError>,
211    ) {
212    }
213
214    /// Create a final report as a Value
215    ///
216    /// Intended to be called after deactivate()
217    #[allow(unused_variables)]
218    fn report(&self, engine_state: &EngineState, debugger_span: Span) -> Result<Value, ShellError> {
219        Ok(Value::nothing(debugger_span))
220    }
221}
222
223/// A debugger that does nothing
224///
225/// Used as a placeholder debugger when not debugging.
226#[derive(Debug)]
227pub struct NoopDebugger;
228
229impl Debugger for NoopDebugger {}