From 45ce9f8cde88a7393719fe44bda96e1036baaddc Mon Sep 17 00:00:00 2001 From: djole Date: Mon, 26 Jan 2026 11:09:31 +0100 Subject: [PATCH 01/12] feat: add vars command to display debug variables Implement debug variable tracking and display in the debugger: - Add DebugVarTracker to track variables throughout execution - Add DebugVarSnapshot for variable state at specific clock cycles - Add stub types for DebugVarInfo and DebugVarLocation (pending new miden-core) - Add resolve_variable_value (currently returns None, pending new miden-core) - Add :vars/:variables/:locals commands to display current variables The vars command shows variables in format "name=value" or "name=location" if the value cannot be resolved. --- crates/engine/src/debug/mod.rs | 2 + crates/engine/src/debug/variables.rs | 214 +++++++++++++++++++++++++++ crates/engine/src/exec/executor.rs | 15 +- crates/engine/src/exec/state.rs | 7 +- src/ui/pages/home.rs | 4 + src/ui/state.rs | 62 +++++++- 6 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 crates/engine/src/debug/variables.rs diff --git a/crates/engine/src/debug/mod.rs b/crates/engine/src/debug/mod.rs index aea38a8..76d3d9a 100644 --- a/crates/engine/src/debug/mod.rs +++ b/crates/engine/src/debug/mod.rs @@ -2,6 +2,7 @@ mod breakpoint; mod memory; mod native_ptr; mod stacktrace; +mod variables; pub use self::{ breakpoint::{Breakpoint, BreakpointType}, @@ -11,4 +12,5 @@ pub use self::{ CallFrame, CallStack, ControlFlowOp, CurrentFrame, OpDetail, ResolvedLocation, StackTrace, StepInfo, }, + variables::{DebugVarInfo, DebugVarSnapshot, DebugVarTracker, resolve_variable_value}, }; diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs new file mode 100644 index 0000000..1193687 --- /dev/null +++ b/crates/engine/src/debug/variables.rs @@ -0,0 +1,214 @@ +use std::{ + cell::RefCell, + collections::BTreeMap, + fmt, + rc::Rc, +}; + +use miden_core::Felt; +use miden_processor::RowIndex; + +/// Location of a debug variable's value. +/// +/// This is a stub type until miden-core provides DebugVarLocation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DebugVarLocation { + /// Variable is on the stack at the given position + Stack(u16), + /// Variable is in memory at the given address + Memory(u32), + /// Variable is a constant + Const(Felt), + /// Variable is a local at the given frame offset + Local(u16), + /// Variable location is computed via an expression + Expression(Vec), +} + +impl fmt::Display for DebugVarLocation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Stack(pos) => write!(f, "stack[{pos}]"), + Self::Memory(addr) => write!(f, "mem[{addr}]"), + Self::Const(felt) => write!(f, "const({})", felt.as_int()), + Self::Local(idx) => write!(f, "local[{idx}]"), + Self::Expression(_) => write!(f, "expr(...)"), + } + } +} + +/// Debug variable information. +/// +/// This is a stub type until miden-core provides DebugVarInfo. +#[derive(Debug, Clone)] +pub struct DebugVarInfo { + name: String, + location: DebugVarLocation, +} + +impl DebugVarInfo { + /// Create a new debug variable info + pub fn new(name: impl Into, location: DebugVarLocation) -> Self { + Self { + name: name.into(), + location, + } + } + + /// Get the variable name + pub fn name(&self) -> &str { + &self.name + } + + /// Get the variable's value location + pub fn value_location(&self) -> &DebugVarLocation { + &self.location + } +} + +/// A snapshot of a debug variable at a specific clock cycle. +#[derive(Debug, Clone)] +pub struct DebugVarSnapshot { + /// The clock cycle when this variable info was recorded. + pub clk: RowIndex, + /// The debug variable information. + pub info: DebugVarInfo, +} + +/// Tracks debug variable information throughout program execution. +/// +/// This structure maintains a mapping from variable names to their most recent +/// location information at each clock cycle. It's designed to work with the +/// debugger to provide source-level variable inspection. +pub struct DebugVarTracker { + /// All debug variable events recorded during execution, keyed by clock cycle. + events: Rc>>>, + /// Current view of variables - maps variable name to most recent info. + current_vars: BTreeMap, + /// The clock cycle up to which we've processed events. + processed_up_to: RowIndex, +} + +impl DebugVarTracker { + /// Create a new tracker using the given shared event store. + pub fn new(events: Rc>>>) -> Self { + Self { + events, + current_vars: BTreeMap::new(), + processed_up_to: RowIndex::from(0), + } + } + + /// Update the tracker state to reflect variables at the given clock cycle. + /// + /// This processes all events up to and including `clk`, updating the + /// current variable state accordingly. + pub fn update_to_cycle(&mut self, clk: RowIndex) { + let events = self.events.borrow(); + + // Process events from processed_up_to to clk + for (event_clk, var_infos) in events.range(self.processed_up_to..=clk) { + for info in var_infos { + let snapshot = DebugVarSnapshot { + clk: *event_clk, + info: info.clone(), + }; + self.current_vars.insert(info.name().to_string(), snapshot); + } + } + + self.processed_up_to = clk; + } + + /// Reset the tracker to the beginning of execution. + pub fn reset(&mut self) { + self.current_vars.clear(); + self.processed_up_to = RowIndex::from(0); + } + + /// Get all currently visible variables. + pub fn current_variables(&self) -> impl Iterator { + self.current_vars.values() + } + + /// Get a specific variable by name. + pub fn get_variable(&self, name: &str) -> Option<&DebugVarSnapshot> { + self.current_vars.get(name) + } + + /// Get the number of tracked variables. + pub fn variable_count(&self) -> usize { + self.current_vars.len() + } + + /// Check if there are any tracked variables. + pub fn has_variables(&self) -> bool { + !self.current_vars.is_empty() + } +} + +/// Resolve a debug variable's value given its location and the current VM state. +/// +/// NOTE: This currently always returns None as it requires miden-core +/// for full debug variable support. +pub fn resolve_variable_value( + _location: &DebugVarLocation, + _stack: &[Felt], + _get_memory: impl Fn(u32) -> Option, + _get_local: impl Fn(u16) -> Option, +) -> Option { + // Variable value resolution requires miden-core + // For now, always return None + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tracker_basic() { + let events: Rc>>> = + Rc::new(Default::default()); + + // Add some events + { + let mut events_mut = events.borrow_mut(); + events_mut.insert( + RowIndex::from(1), + vec![DebugVarInfo::new("x", DebugVarLocation::Stack(0))], + ); + events_mut.insert( + RowIndex::from(5), + vec![DebugVarInfo::new("y", DebugVarLocation::Stack(1))], + ); + } + + let mut tracker = DebugVarTracker::new(events); + + // Initially no variables + assert_eq!(tracker.variable_count(), 0); + + // Process up to cycle 3 + tracker.update_to_cycle(RowIndex::from(3)); + assert_eq!(tracker.variable_count(), 1); + assert!(tracker.get_variable("x").is_some()); + assert!(tracker.get_variable("y").is_none()); + + // Process up to cycle 10 + tracker.update_to_cycle(RowIndex::from(10)); + assert_eq!(tracker.variable_count(), 2); + assert!(tracker.get_variable("x").is_some()); + assert!(tracker.get_variable("y").is_some()); + + // Verify resolve_variable_value returns None + let x_snapshot = tracker.get_variable("x").unwrap(); + let value = resolve_variable_value( + x_snapshot.info.value_location(), + &[Felt::new(42)], + |_| None, + |_| None, + ); + assert!(value.is_none(), "resolve_variable_value should return None for now"); + } +} diff --git a/crates/engine/src/exec/executor.rs b/crates/engine/src/exec/executor.rs index 378edcd..05bb18d 100644 --- a/crates/engine/src/exec/executor.rs +++ b/crates/engine/src/exec/executor.rs @@ -21,7 +21,7 @@ use miden_processor::{ }; use super::{DebugExecutor, DebuggerHost, ExecutionConfig, ExecutionTrace, TraceEvent}; -use crate::{debug::CallStack, felt::FromMidenRepr}; +use crate::{debug::{CallStack, DebugVarInfo, DebugVarTracker}, felt::FromMidenRepr}; /// The [Executor] is responsible for executing a program with the Miden VM. /// @@ -163,6 +163,12 @@ impl Executor { assertion_events.borrow_mut().insert(clk, event); }); + // Set up debug variable tracking + // Note: Currently no debug var events are emitted (requires new miden-core), + // but we set up the infrastructure for when they become available. + let debug_var_events: Rc>>> = + Rc::new(Default::default()); + let mut processor = FastProcessor::new(self.stack) .with_advice(self.advice) .with_options(self.options) @@ -175,6 +181,7 @@ impl Executor { .expect("failed to get initial resume context"); let callstack = CallStack::new(trace_events); + let debug_vars = DebugVarTracker::new(debug_var_events); DebugExecutor { processor, host, @@ -187,6 +194,7 @@ impl Executor { root_context, current_context: root_context, callstack, + debug_vars, recent: VecDeque::with_capacity(5), cycle: 0, stopped: false, @@ -219,6 +227,9 @@ impl Executor { } host.set_event_replay(event_replay); + let debug_var_events: Rc>>> = + Rc::new(Default::default()); + let trace_events: Rc>> = Rc::new(Default::default()); let frame_start_events = Rc::clone(&trace_events); host.register_trace_handler(TraceEvent::FrameStart, move |clk, event| { @@ -245,6 +256,7 @@ impl Executor { .expect("failed to get initial resume context"); let callstack = CallStack::new(trace_events); + let debug_vars = DebugVarTracker::new(debug_var_events); DebugExecutor { processor, host, @@ -257,6 +269,7 @@ impl Executor { root_context, current_context: root_context, callstack, + debug_vars, recent: VecDeque::with_capacity(5), cycle: 0, stopped: false, diff --git a/crates/engine/src/exec/state.rs b/crates/engine/src/exec/state.rs index c1ece65..d978a43 100644 --- a/crates/engine/src/exec/state.rs +++ b/crates/engine/src/exec/state.rs @@ -10,7 +10,7 @@ use miden_processor::{ }; use super::{DebuggerHost, ExecutionTrace}; -use crate::debug::{CallFrame, CallStack, ControlFlowOp, StepInfo}; +use crate::debug::{CallFrame, CallStack, ControlFlowOp, DebugVarTracker, StepInfo}; /// Resolve a future that is expected to complete immediately (synchronous host methods). /// @@ -60,6 +60,8 @@ pub struct DebugExecutor { pub current_context: ContextId, /// The current call stack pub callstack: CallStack, + /// Debug variable tracker for source-level variable inspection + pub debug_vars: DebugVarTracker, /// A sliding window of the last 5 operations successfully executed by the VM pub recent: VecDeque, /// The current clock cycle @@ -195,6 +197,9 @@ impl DebugExecutor { }; let exited = self.callstack.next(&step_info); + // Update debug variable tracker to current clock cycle + self.debug_vars.update_to_cycle(RowIndex::from(self.cycle as u32)); + Ok(exited) } Ok(None) => { diff --git a/src/ui/pages/home.rs b/src/ui/pages/home.rs index ee728aa..48f600b 100644 --- a/src/ui/pages/home.rs +++ b/src/ui/pages/home.rs @@ -148,6 +148,10 @@ impl Page for Home { "debug" => { actions.push(Some(Action::ShowDebug)); } + "vars" | "variables" | "locals" => { + let result = state.format_variables(); + actions.push(Some(Action::StatusLine(result))); + } invalid => { log::debug!("unknown command: '{invalid}'"); actions.push(Some(Action::TimedStatusLine("unknown command".into(), 1))) diff --git a/src/ui/state.rs b/src/ui/state.rs index 396f09f..67e8fe7 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -11,7 +11,7 @@ use miden_processor::{ use crate::{ config::DebuggerConfig, - debug::{Breakpoint, BreakpointType, ReadMemoryExpr}, + debug::{Breakpoint, BreakpointType, ReadMemoryExpr, resolve_variable_value}, exec::{DebugExecutor, ExecutionTrace, Executor}, input::InputFile, }; @@ -664,6 +664,66 @@ impl State { Ok(output) } + + /// Format the current debug variables as a string for display. + /// + /// Returns a string describing all tracked variables and their current values, + /// or a message indicating no variables are being tracked. + pub fn format_variables(&self) -> String { + use core::fmt::Write; + + let debug_vars = &self.executor.debug_vars; + + if !debug_vars.has_variables() { + return "No debug variables tracked".to_string(); + } + + let mut output = String::new(); + let stack: Vec = self + .executor + .last + .as_ref() + .map(|state| state.stack.clone()) + .unwrap_or_default(); + + let context = self.executor.current_context; + let cycle = miden_processor::RowIndex::from(self.executor.cycle); + + for var_snapshot in debug_vars.current_variables() { + if !output.is_empty() { + output.push_str(", "); + } + + let name = var_snapshot.info.name(); + let location = var_snapshot.info.value_location(); + + // Try to resolve the variable value + let value = resolve_variable_value( + location, + &stack, + |addr| { + self.execution_trace + .read_memory_element_in_context(addr, context, cycle) + }, + |_idx| { + // Local resolution would need FMP calculation + // For now, return None + None + }, + ); + + match value { + Some(felt) => { + write!(&mut output, "{name}={}", felt.as_int()).unwrap(); + } + None => { + write!(&mut output, "{name}={location}").unwrap(); + } + } + } + + output + } } // DAP CLIENT MODE From 0be88dfa224f6fd9a481c89c4274b387c5778fdf Mon Sep 17 00:00:00 2001 From: djole Date: Mon, 26 Jan 2026 11:16:15 +0100 Subject: [PATCH 02/12] chore: apply rustfmt formatting --- crates/engine/src/debug/variables.rs | 7 +------ crates/engine/src/exec/executor.rs | 5 ++++- src/ui/state.rs | 13 +++---------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index 1193687..fe9f706 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -1,9 +1,4 @@ -use std::{ - cell::RefCell, - collections::BTreeMap, - fmt, - rc::Rc, -}; +use std::{cell::RefCell, collections::BTreeMap, fmt, rc::Rc}; use miden_core::Felt; use miden_processor::RowIndex; diff --git a/crates/engine/src/exec/executor.rs b/crates/engine/src/exec/executor.rs index 05bb18d..1bc6dd7 100644 --- a/crates/engine/src/exec/executor.rs +++ b/crates/engine/src/exec/executor.rs @@ -21,7 +21,10 @@ use miden_processor::{ }; use super::{DebugExecutor, DebuggerHost, ExecutionConfig, ExecutionTrace, TraceEvent}; -use crate::{debug::{CallStack, DebugVarInfo, DebugVarTracker}, felt::FromMidenRepr}; +use crate::{ + debug::{CallStack, DebugVarInfo, DebugVarTracker}, + felt::FromMidenRepr, +}; /// The [Executor] is responsible for executing a program with the Miden VM. /// diff --git a/src/ui/state.rs b/src/ui/state.rs index 67e8fe7..d9cd7cb 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -679,12 +679,8 @@ impl State { } let mut output = String::new(); - let stack: Vec = self - .executor - .last - .as_ref() - .map(|state| state.stack.clone()) - .unwrap_or_default(); + let stack: Vec = + self.executor.last.as_ref().map(|state| state.stack.clone()).unwrap_or_default(); let context = self.executor.current_context; let cycle = miden_processor::RowIndex::from(self.executor.cycle); @@ -701,10 +697,7 @@ impl State { let value = resolve_variable_value( location, &stack, - |addr| { - self.execution_trace - .read_memory_element_in_context(addr, context, cycle) - }, + |addr| self.execution_trace.read_memory_element_in_context(addr, context, cycle), |_idx| { // Local resolution would need FMP calculation // For now, return None From d53f9de0cab2150923efa9482e6a1c26c2807111 Mon Sep 17 00:00:00 2001 From: djole Date: Mon, 30 Mar 2026 17:07:13 +0200 Subject: [PATCH 03/12] fix: adapt debug variable tracking to v0.22 API --- crates/engine/src/debug/variables.rs | 4 ++-- src/ui/state.rs | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index fe9f706..9a1ed64 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, collections::BTreeMap, fmt, rc::Rc}; use miden_core::Felt; -use miden_processor::RowIndex; +use miden_processor::trace::RowIndex; /// Location of a debug variable's value. /// @@ -25,7 +25,7 @@ impl fmt::Display for DebugVarLocation { match self { Self::Stack(pos) => write!(f, "stack[{pos}]"), Self::Memory(addr) => write!(f, "mem[{addr}]"), - Self::Const(felt) => write!(f, "const({})", felt.as_int()), + Self::Const(felt) => write!(f, "const({})", felt.as_canonical_u64()), Self::Local(idx) => write!(f, "local[{idx}]"), Self::Expression(_) => write!(f, "expr(...)"), } diff --git a/src/ui/state.rs b/src/ui/state.rs index d9cd7cb..a405476 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -672,18 +672,23 @@ impl State { pub fn format_variables(&self) -> String { use core::fmt::Write; - let debug_vars = &self.executor.debug_vars; + let executor = self.executor(); + let debug_vars = &executor.debug_vars; if !debug_vars.has_variables() { return "No debug variables tracked".to_string(); } let mut output = String::new(); - let stack: Vec = - self.executor.last.as_ref().map(|state| state.stack.clone()).unwrap_or_default(); + let stack = executor.current_stack.clone(); + let context = executor.current_context; + let cycle = miden_processor::trace::RowIndex::from(executor.cycle as u32); - let context = self.executor.current_context; - let cycle = miden_processor::RowIndex::from(self.executor.cycle); + let execution_trace = match &self.session { + SessionState::Local(local) => Some(&local.execution_trace), + #[cfg(feature = "dap")] + SessionState::Remote(_) => None, + }; for var_snapshot in debug_vars.current_variables() { if !output.is_empty() { @@ -697,7 +702,10 @@ impl State { let value = resolve_variable_value( location, &stack, - |addr| self.execution_trace.read_memory_element_in_context(addr, context, cycle), + |addr| { + execution_trace + .and_then(|t| t.read_memory_element_in_context(addr, context, cycle)) + }, |_idx| { // Local resolution would need FMP calculation // For now, return None @@ -707,7 +715,7 @@ impl State { match value { Some(felt) => { - write!(&mut output, "{name}={}", felt.as_int()).unwrap(); + write!(&mut output, "{name}={}", felt.as_canonical_u64()).unwrap(); } None => { write!(&mut output, "{name}={location}").unwrap(); From 2487e77238745fc642a2c41cc83562fa36097a96 Mon Sep 17 00:00:00 2001 From: djole Date: Tue, 31 Mar 2026 17:12:07 +0200 Subject: [PATCH 04/12] fix: FrameBase resolution Also filter compiler locals and fix light terminal rendering --- Cargo.lock | 382 +++++++++------------------ Cargo.toml | 13 + crates/engine/src/debug/mod.rs | 2 +- crates/engine/src/debug/variables.rs | 105 +++----- crates/engine/src/exec/executor.rs | 3 +- crates/engine/src/exec/state.rs | 16 +- src/ui/pages/home.rs | 7 +- src/ui/panes/source_code.rs | 52 ++-- src/ui/panes/stacktrace.rs | 2 +- src/ui/state.rs | 54 ++-- src/ui/syntax_highlighting.rs | 27 +- 11 files changed, 273 insertions(+), 390 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27ae6c1..8f4747e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,16 +166,16 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures 0.3.0", + "cpufeatures 0.2.17", ] [[package]] @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.59" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -720,17 +720,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - [[package]] name = "errno" version = "0.3.14" @@ -754,9 +743,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -1062,9 +1051,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.13.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -1169,9 +1158,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.94" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -1244,9 +1233,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libm" @@ -1326,9 +1315,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miden-air" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15646ebc95906b2a7cb66711d1e184f53fd6edc2605730bbcf0c2a129f792cf" +version = "0.22.0" dependencies = [ "miden-core", "miden-crypto", @@ -1339,25 +1326,19 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6013b3a390e0dcb29242f4480a7727965887bbf0903466c88f362b4cb20c0e" +version = "0.22.0" dependencies = [ "log", "miden-assembly-syntax", "miden-core", "miden-mast-package", - "miden-package-registry", - "miden-project", "smallvec", "thiserror 2.0.18", ] [[package]] name = "miden-assembly-syntax" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "996156b8f7c5fe6be17dea71089c6d7985c2dec1e3a4fec068b1dfc690e25df5" +version = "0.22.0" dependencies = [ "aho-corasick", "lalrpop", @@ -1370,17 +1351,14 @@ dependencies = [ "proptest", "regex", "rustc_version 0.4.1", - "semver 1.0.28", - "serde", + "semver 1.0.27", "smallvec", "thiserror 2.0.18", ] [[package]] name = "miden-core" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdec54a321cdf3d23e9ef615e91cb858038c6b4d4202507bdec048fc6d7763e4" +version = "0.22.0" dependencies = [ "derive_more", "itertools 0.14.0", @@ -1389,27 +1367,25 @@ dependencies = [ "miden-formatting", "miden-utils-core-derive", "miden-utils-indexing", - "miden-utils-sync", + "miden-utils-sync 0.22.0", "num-derive", "num-traits", - "serde", "thiserror 2.0.18", ] [[package]] name = "miden-core-lib" -version = "0.22.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "621e8fa911a790bcf3cd3aedce80bc10922a19d6181f08ff3ca078f955cff70b" +checksum = "82595fabb062315c32f6fc11c31755d3e5c6f8bc8c67d35154a067397d65b1de" dependencies = [ "env_logger", "fs-err", "miden-assembly", "miden-core", "miden-crypto", - "miden-package-registry", "miden-processor", - "miden-utils-sync", + "miden-utils-sync 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", ] @@ -1542,19 +1518,17 @@ dependencies = [ [[package]] name = "miden-debug-types" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6e50274d11c80b901cf6c90362de8c98c8c8ad6030c80624d683b63d899a0fb" +version = "0.22.0" dependencies = [ "memchr", "miden-crypto", "miden-formatting", "miden-miette", "miden-utils-indexing", - "miden-utils-sync", + "miden-utils-sync 0.22.0", "paste", "serde", - "serde_spanned 1.1.1", + "serde_spanned 1.1.0", "thiserror 2.0.18", ] @@ -1587,15 +1561,12 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc8b2e3447fcde1f0e6b76e5219f129517639772cb02ca543177f0584e315288" +version = "0.22.0" dependencies = [ "derive_more", "miden-assembly-syntax", "miden-core", "miden-debug-types", - "serde", "thiserror 2.0.18", ] @@ -1635,26 +1606,9 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "miden-package-registry" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969ba3942052e52b3968e34dbd1c52c707e75777ee42ebdae2c8f57af56cf6cf" -dependencies = [ - "miden-assembly-syntax", - "miden-core", - "miden-mast-package", - "pubgrub", - "serde", - "smallvec", - "thiserror 2.0.18", -] - [[package]] name = "miden-processor" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ec6cecbf22bd92b73a931ee80b424e46b8b7cdf4f2f3c364c25c5c15d2840da" +version = "0.22.0" dependencies = [ "itertools 0.14.0", "miden-air", @@ -1669,27 +1623,10 @@ dependencies = [ "tracing", ] -[[package]] -name = "miden-project" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3840520c01881534fbbceb6b3687ec1c407fbaf310a35ce415fd3510abc52fdb" -dependencies = [ - "miden-assembly-syntax", - "miden-core", - "miden-mast-package", - "miden-package-registry", - "serde", - "serde-untagged", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", -] - [[package]] name = "miden-protocol" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595b3d43ceb562d05e248a6c52bdc2992dc270b60d6d0e8c0a6480aa30672b8e" +version = "0.14.0" +source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" dependencies = [ "bech32", "fs-err", @@ -1702,20 +1639,19 @@ dependencies = [ "miden-mast-package", "miden-processor", "miden-protocol-macros", - "miden-utils-sync", + "miden-utils-sync 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "miden-verifier", "rand 0.9.2", "regex", - "semver 1.0.28", + "semver 1.0.27", "thiserror 2.0.18", "walkdir", ] [[package]] name = "miden-protocol-macros" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c287782953c94452c2f7431feff137e4fe8b8d1eb5aa9112eab83a6f11c8f2" +version = "0.14.0" +source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" dependencies = [ "proc-macro2", "quote", @@ -1724,9 +1660,9 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.22.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb2c94e36f57684d7fa0cd382adeedc1728d502dbbe69ad1c12f4a931f45511" +checksum = "15462425359e87540d92e277cf1174a85a174ca433bd63d27286f65ab318f2d4" dependencies = [ "bincode", "miden-air", @@ -1752,9 +1688,8 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f63cd9264dc1f9f124fe644ee631828cc9bfd71022a75cd5bc1678f3ba7b56" +version = "0.14.0" +source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" dependencies = [ "fs-err", "miden-assembly", @@ -1789,9 +1724,8 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e894e952e2819545e9351f7427779f82538e51553dfaca3294301ff308086497" +version = "0.14.0" +source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" dependencies = [ "miden-processor", "miden-protocol", @@ -1803,9 +1737,7 @@ dependencies = [ [[package]] name = "miden-utils-core-derive" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3846c8674ccec0c37005f99c1a599a24790ba2a5e5f4e1c7aec5f456821df835" +version = "0.22.0" dependencies = [ "proc-macro2", "quote", @@ -1814,9 +1746,7 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397f5d1e8679cf17cf7713ffd9654840791a6ed5818b025bbc2fbfdce846579a" +version = "0.22.0" dependencies = [ "miden-crypto", "miden-debug-types", @@ -1827,20 +1757,15 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8834e76299686bcce3de1685158aa4cff49b7fa5e0e00a6cc811e8f2cf5775f" +version = "0.22.0" dependencies = [ "miden-crypto", - "serde", "thiserror 2.0.18", ] [[package]] name = "miden-utils-sync" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9e9747e9664c1a0997bb040ae291306ea0a1c74a572141ec66cec855c1b0e8" +version = "0.22.0" dependencies = [ "lock_api", "loom", @@ -1848,11 +1773,22 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "miden-utils-sync" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529c1c173506f30d3949f7a54b65f1eb318098e37ed5730a1bb9027eee2fa4b" +dependencies = [ + "lock_api", + "loom", + "once_cell", +] + [[package]] name = "miden-verifier" -version = "0.22.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4580df640d889c9f3c349cd2268968e44a99a8cf0df6c36ae5b1fb273712b00" +checksum = "997c842047ffa2d011eb65bf638a3135b2d52bce5b20770fcc6040f1b48c624a" dependencies = [ "bincode", "miden-air", @@ -1889,9 +1825,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", @@ -2056,9 +1992,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p3-air" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2ec9cbfc642fc5173817287c3f8b789d07743b5f7e812d058b7a03e344f9ab" +checksum = "9ebc58ec27a174420348b3f04dba836fa2e5b5fe8df74601087417352757c643" dependencies = [ "p3-field", "p3-matrix", @@ -2067,9 +2003,9 @@ dependencies = [ [[package]] name = "p3-blake3" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b667f43b19499dd939c9e2553aa95688936a88360d50117dae3c8848d07dbc70" +checksum = "b3cacb38c29fbee71fe3e5c6c0a1073632e46dc3e93fbdc50ab4e4fac137b525" dependencies = [ "blake3", "p3-symmetric", @@ -2078,9 +2014,9 @@ dependencies = [ [[package]] name = "p3-challenger" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" +checksum = "af9bbcb18fe90271668259aacfc43455e328673c2b5c926cff0663edc8653e4d" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -2092,9 +2028,9 @@ dependencies = [ [[package]] name = "p3-commit" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916ae7989d5c3b49f887f5c55b2f9826bdbb81aaebf834503c4145d8b267c829" +checksum = "14d07b50c6f6d3bc89ed7c54ae0c569fb4caaa58263fd389dc02fb1b0a6378fa" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2105,9 +2041,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" +checksum = "17e7ba0dc20be075eab3f88f0cb820a0901f86218a1c46134e7c817d41597989" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2120,9 +2056,9 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" +checksum = "9b8533e6c2f4d0cc61fd2ae5299bb83316898e535f47291808d37e4d666ba088" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -2136,9 +2072,9 @@ dependencies = [ [[package]] name = "p3-goldilocks" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca1081f5c47b940f2d75a11c04f62ea1cc58a5d480dd465fef3861c045c63cd" +checksum = "d8102a8c85acee1f896c3764bef5fac908e6026dadfc557c185294970cce0746" dependencies = [ "num-bigint", "p3-challenger", @@ -2156,9 +2092,9 @@ dependencies = [ [[package]] name = "p3-keccak" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcf27615ece1995e4fcf4c69740f1cf515d1481367a20b4b3ce7f4f1b8d70f7" +checksum = "b65d30dd586d2855906a01c3414c155c2d564f6677d1b51f04186dcac080f757" dependencies = [ "p3-symmetric", "p3-util", @@ -2167,9 +2103,9 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" +checksum = "72bb78444459155c2e4711d71abbfef7b04cc2ba1fa83751ccab241b01957095" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2182,15 +2118,15 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" +checksum = "70a0a54345917f500130a9986fa5ff9ecbc26f0c6313080b35b713e26ddc8053" [[package]] name = "p3-mds" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" +checksum = "3cd514bf3e9bf9f1b7db2db96e5bd2972d9963dd62430de1e193d74522ae96a6" dependencies = [ "p3-dft", "p3-field", @@ -2297,9 +2233,9 @@ dependencies = [ [[package]] name = "p3-monty-31" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" +checksum = "0d9340a650f07a6cd42a4e877017ba7b206df87fe50dfc3cf110f01a3c370bd1" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -2321,9 +2257,9 @@ dependencies = [ [[package]] name = "p3-poseidon1" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" +checksum = "6dd56ae3a51ded1b77f7b1b21d0b157ae82b9d5ca8f2cba347c0b821fe771a79" dependencies = [ "p3-field", "p3-symmetric", @@ -2332,9 +2268,9 @@ dependencies = [ [[package]] name = "p3-poseidon2" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" +checksum = "858aa1c33ec983dfbb8cfc553a213de19d8fde96485e54e6e952b9ac5e70bd4e" dependencies = [ "p3-field", "p3-mds", @@ -2345,9 +2281,9 @@ dependencies = [ [[package]] name = "p3-symmetric" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" +checksum = "1a9a3b20bb8104e52d45219a78d80654c8ac6a4781be0eaa3f3e999f5ae4b9b2" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2357,9 +2293,9 @@ dependencies = [ [[package]] name = "p3-util" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" +checksum = "9f24495d9cd64693165a9f1b3da0758395ad6d25d2d44dd740bdb34c2bce0c53" dependencies = [ "serde", "transpose", @@ -2486,17 +2422,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "priority-queue" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" -dependencies = [ - "equivalent", - "indexmap", - "serde", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2525,20 +2450,6 @@ dependencies = [ "unarray", ] -[[package]] -name = "pubgrub" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f5df7e552bc7edd075f5783a87fbfc21d6a546e32c16985679c488c18192d83" -dependencies = [ - "indexmap", - "log", - "priority-queue", - "rustc-hash", - "thiserror 2.0.18", - "version-ranges", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -2755,12 +2666,6 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - [[package]] name = "rustc_version" version = "0.2.3" @@ -2776,7 +2681,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.28", + "semver 1.0.27", ] [[package]] @@ -2875,9 +2780,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.28" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", "serde_core", @@ -2899,18 +2804,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-untagged" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" -dependencies = [ - "erased-serde", - "serde", - "serde_core", - "typeid", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -2966,9 +2859,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -3052,9 +2945,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "siphasher" @@ -3337,9 +3230,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.51.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "pin-project-lite", "tokio-macros", @@ -3347,9 +3240,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", @@ -3384,17 +3277,17 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 1.1.1+spec-1.1.0", + "serde_spanned 1.1.0", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.1", + "winnow 1.0.0", ] [[package]] @@ -3408,9 +3301,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" dependencies = [ "serde_core", ] @@ -3431,11 +3324,11 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.0", ] [[package]] @@ -3446,9 +3339,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "tracing" @@ -3534,7 +3427,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.0+spec-1.1.0", ] [[package]] @@ -3547,12 +3440,6 @@ dependencies = [ "unicode-width 0.2.0", ] -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.19.0" @@ -3634,15 +3521,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "version-ranges" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3595ffe225639f1e0fd8d7269dcc05d2fbfea93cfac2fea367daf1adb60aae91" -dependencies = [ - "smallvec", -] - [[package]] name = "version_check" version = "0.9.5" @@ -3703,9 +3581,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.117" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -3716,9 +3594,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.117" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3726,9 +3604,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.117" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", @@ -3739,9 +3617,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.117" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -3777,7 +3655,7 @@ dependencies = [ "bitflags", "hashbrown 0.15.5", "indexmap", - "semver 1.0.28", + "semver 1.0.27", ] [[package]] @@ -3928,9 +3806,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" [[package]] name = "wit-bindgen" @@ -4012,7 +3890,7 @@ dependencies = [ "id-arena", "indexmap", "log", - "semver 1.0.28", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -4041,18 +3919,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2deac3b..c8f8871 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,3 +89,16 @@ proptest = { version = "1.4", optional = true } miden-crypto = { version = "=0.23.0", default-features = false } +[patch.crates-io] +miden-assembly = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-assembly-syntax = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-core = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-core-lib = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-debug-types = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-mast-package = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-package-registry = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-processor = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-project = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-utils-diagnostics = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-utils-indexing = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } +miden-utils-sync = { git = "https://github.com/walnuthq/miden-vm", branch = "fix/debug-var-dedup-crash" } diff --git a/crates/engine/src/debug/mod.rs b/crates/engine/src/debug/mod.rs index 76d3d9a..7f43369 100644 --- a/crates/engine/src/debug/mod.rs +++ b/crates/engine/src/debug/mod.rs @@ -12,5 +12,5 @@ pub use self::{ CallFrame, CallStack, ControlFlowOp, CurrentFrame, OpDetail, ResolvedLocation, StackTrace, StepInfo, }, - variables::{DebugVarInfo, DebugVarSnapshot, DebugVarTracker, resolve_variable_value}, + variables::{DebugVarSnapshot, DebugVarTracker, resolve_variable_value}, }; diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index 9a1ed64..f8dffe9 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -1,66 +1,8 @@ -use std::{cell::RefCell, collections::BTreeMap, fmt, rc::Rc}; +use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; -use miden_core::Felt; +use miden_core::{Felt, operations::DebugVarInfo, operations::DebugVarLocation}; use miden_processor::trace::RowIndex; -/// Location of a debug variable's value. -/// -/// This is a stub type until miden-core provides DebugVarLocation. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum DebugVarLocation { - /// Variable is on the stack at the given position - Stack(u16), - /// Variable is in memory at the given address - Memory(u32), - /// Variable is a constant - Const(Felt), - /// Variable is a local at the given frame offset - Local(u16), - /// Variable location is computed via an expression - Expression(Vec), -} - -impl fmt::Display for DebugVarLocation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Stack(pos) => write!(f, "stack[{pos}]"), - Self::Memory(addr) => write!(f, "mem[{addr}]"), - Self::Const(felt) => write!(f, "const({})", felt.as_canonical_u64()), - Self::Local(idx) => write!(f, "local[{idx}]"), - Self::Expression(_) => write!(f, "expr(...)"), - } - } -} - -/// Debug variable information. -/// -/// This is a stub type until miden-core provides DebugVarInfo. -#[derive(Debug, Clone)] -pub struct DebugVarInfo { - name: String, - location: DebugVarLocation, -} - -impl DebugVarInfo { - /// Create a new debug variable info - pub fn new(name: impl Into, location: DebugVarLocation) -> Self { - Self { - name: name.into(), - location, - } - } - - /// Get the variable name - pub fn name(&self) -> &str { - &self.name - } - - /// Get the variable's value location - pub fn value_location(&self) -> &DebugVarLocation { - &self.location - } -} - /// A snapshot of a debug variable at a specific clock cycle. #[derive(Debug, Clone)] pub struct DebugVarSnapshot { @@ -94,6 +36,13 @@ impl DebugVarTracker { } } + /// Record debug variable events at the given clock cycle. + pub fn record_events(&self, clk: RowIndex, infos: Vec) { + if !infos.is_empty() { + self.events.borrow_mut().entry(clk).or_default().extend(infos); + } + } + /// Update the tracker state to reflect variables at the given clock cycle. /// /// This processes all events up to and including `clk`, updating the @@ -143,18 +92,30 @@ impl DebugVarTracker { } /// Resolve a debug variable's value given its location and the current VM state. -/// -/// NOTE: This currently always returns None as it requires miden-core -/// for full debug variable support. pub fn resolve_variable_value( - _location: &DebugVarLocation, - _stack: &[Felt], - _get_memory: impl Fn(u32) -> Option, - _get_local: impl Fn(u16) -> Option, + location: &DebugVarLocation, + stack: &[Felt], + get_memory: impl Fn(u32) -> Option, + _get_local: impl Fn(i16) -> Option, ) -> Option { - // Variable value resolution requires miden-core - // For now, always return None - None + match location { + DebugVarLocation::Stack(pos) => stack.get(*pos as usize).copied(), + DebugVarLocation::Memory(addr) => get_memory(*addr), + DebugVarLocation::Const(felt) => Some(*felt), + DebugVarLocation::Local(offset) => _get_local(*offset), + DebugVarLocation::FrameBase { global_index, byte_offset } => { + // global_index was resolved to a Miden byte address during compilation. + // Convert to element address (÷4) to read the stack pointer value. + let sp_elem_addr = *global_index / 4; + let base = get_memory(sp_elem_addr)?; + // The stack pointer value is also a byte address; apply byte_offset, + // then convert to element address to read the variable's value. + let byte_addr = base.as_canonical_u64() as i64 + byte_offset; + let elem_addr = (byte_addr / 4) as u32; + get_memory(elem_addr) + } + DebugVarLocation::Expression(_) => None, + } } #[cfg(test)] @@ -196,7 +157,7 @@ mod tests { assert!(tracker.get_variable("x").is_some()); assert!(tracker.get_variable("y").is_some()); - // Verify resolve_variable_value returns None + // Verify resolve_variable_value resolves stack values let x_snapshot = tracker.get_variable("x").unwrap(); let value = resolve_variable_value( x_snapshot.info.value_location(), @@ -204,6 +165,6 @@ mod tests { |_| None, |_| None, ); - assert!(value.is_none(), "resolve_variable_value should return None for now"); + assert_eq!(value, Some(Felt::new(42))); } } diff --git a/crates/engine/src/exec/executor.rs b/crates/engine/src/exec/executor.rs index 1bc6dd7..b2c7da7 100644 --- a/crates/engine/src/exec/executor.rs +++ b/crates/engine/src/exec/executor.rs @@ -22,9 +22,10 @@ use miden_processor::{ use super::{DebugExecutor, DebuggerHost, ExecutionConfig, ExecutionTrace, TraceEvent}; use crate::{ - debug::{CallStack, DebugVarInfo, DebugVarTracker}, + debug::{CallStack, DebugVarTracker}, felt::FromMidenRepr, }; +use miden_core::operations::DebugVarInfo; /// The [Executor] is responsible for executing a program with the Miden VM. /// diff --git a/crates/engine/src/exec/state.rs b/crates/engine/src/exec/state.rs index d978a43..e5bf057 100644 --- a/crates/engine/src/exec/state.rs +++ b/crates/engine/src/exec/state.rs @@ -160,6 +160,18 @@ impl DebugExecutor { let asmop = node_id .and_then(|nid| resume_ctx.current_forest().get_assembly_op(nid, op_idx).cloned()); + // Look up debug vars from MAST forest for the current operation + let debug_var_infos: Vec<_> = if let (Some(nid), Some(idx)) = (node_id, op_idx) { + let forest = resume_ctx.current_forest(); + forest + .debug_vars_for_operation(nid, idx) + .iter() + .filter_map(|vid| forest.debug_var(*vid).cloned()) + .collect() + } else { + vec![] + }; + // Execute one step match poll_immediately(self.processor.step(&mut self.host, resume_ctx)) { Ok(Some(new_ctx)) => { @@ -197,7 +209,9 @@ impl DebugExecutor { }; let exited = self.callstack.next(&step_info); - // Update debug variable tracker to current clock cycle + // Record and process debug variable events + self.debug_vars + .record_events(RowIndex::from(self.cycle as u32), debug_var_infos); self.debug_vars.update_to_cycle(RowIndex::from(self.cycle as u32)); Ok(exited) diff --git a/src/ui/pages/home.rs b/src/ui/pages/home.rs index 48f600b..ffa25a0 100644 --- a/src/ui/pages/home.rs +++ b/src/ui/pages/home.rs @@ -135,6 +135,11 @@ impl Page for Home { }, Err(err) => actions.push(Some(Action::TimedStatusLine(err, 5))), }, + "vars" | "variables" | "locals" => { + let show_all = rest.trim() == "all"; + let result = state.format_variables(show_all); + actions.push(Some(Action::StatusLine(result))); + } _ => { log::debug!("unknown command with arguments: '{cmd} {args}'"); actions.push(Some(Action::TimedStatusLine("unknown command".into(), 1))) @@ -149,7 +154,7 @@ impl Page for Home { actions.push(Some(Action::ShowDebug)); } "vars" | "variables" | "locals" => { - let result = state.format_variables(); + let result = state.format_variables(false); actions.push(Some(Action::StatusLine(result))); } invalid => { diff --git a/src/ui/panes/source_code.rs b/src/ui/panes/source_code.rs index baec576..b8ee63d 100644 --- a/src/ui/panes/source_code.rs +++ b/src/ui/panes/source_code.rs @@ -138,38 +138,20 @@ impl Default for Theme { Self { focused_border_style: Style::default(), current_line: Style::default() - .bg(Color::Black) - .fg(Color::White) - .add_modifier(Modifier::BOLD), + .add_modifier(Modifier::BOLD | Modifier::REVERSED), current_span: Style::default() - .fg(Color::White) - .bg(Color::DarkGray) - .add_modifier(Modifier::BOLD), + .add_modifier(Modifier::BOLD | Modifier::REVERSED), line_number: Style::default(), gutter_border: Style::default(), } } } impl Theme { - pub fn patch_from_syntect(&mut self, theme: &syntect::highlighting::Theme) { - use crate::ui::syntax_highlighting::convert_color; - if let Some(bg) = theme.settings.line_highlight.map(convert_color) { - self.current_line.bg = Some(bg); - } - if let Some(bg) = theme.settings.selection.map(convert_color) { - self.current_span.bg = Some(bg); - } - if let Some(fg) = theme.settings.selection_foreground.map(convert_color) { - self.current_span.fg = Some(fg); - } - if let Some(bg) = theme.settings.gutter.map(convert_color) { - self.line_number.bg = Some(bg); - self.gutter_border.bg = Some(bg); - } - if let Some(fg) = theme.settings.gutter_foreground.map(convert_color) { - self.line_number.fg = Some(fg); - self.gutter_border.fg = Some(fg); - } + pub fn patch_from_syntect(&mut self, _theme: &syntect::highlighting::Theme) { + // Intentionally do NOT apply the syntect theme's line_highlight, selection, + // or gutter colors here. The defaults use REVERSED which works on both + // light and dark terminals. The syntect dark theme colors would make text + // invisible on light terminals. } } @@ -303,7 +285,7 @@ impl Pane for SourceCodePane { if let Some(loc) = self.current_location(state) { let source_id = loc.source_file.id(); if source_id != self.current_source_id { - self.highlight_file(&loc); + self.current_file = Some(self.highlight_file(&loc)); self.current_source_id = source_id; self.num_lines = loc.source_file.line_count() as u32; self.selected_line = loc.line; @@ -387,14 +369,18 @@ impl Pane for SourceCodePane { .unwrap(); let selection_start = core::cmp::max(span.start(), line_span.start); let selection_end = core::cmp::min(span.end(), line_span.end); - let selected_span = SourceSpan::new(span.source_id(), selection_start..selection_end); - let selected = selected_span.into_slice_index(); - let selected = if selected_span.is_empty() { - // Select the closest character to the span - let start = selected.start - line_span.start.to_usize(); - start..start + // Guard: if the span doesn't overlap this line, select nothing + let selected = if selection_start >= selection_end { + 0..0 } else { - (selected.start - line_span.start.to_usize())..(selected.end - line_span.end.to_usize()) + let selected_span = SourceSpan::new(span.source_id(), selection_start..selection_end); + let selected = selected_span.into_slice_index(); + if selected_span.is_empty() { + let start = selected.start - line_span.start.to_usize(); + start..start + } else { + (selected.start - line_span.start.to_usize())..(selected.end - line_span.start.to_usize()) + } }; let mut parts = syntect::util::modify_range( selected_line_deconstructed.as_slice(), diff --git a/src/ui/panes/stacktrace.rs b/src/ui/panes/stacktrace.rs index f9e790e..97b7f45 100644 --- a/src/ui/panes/stacktrace.rs +++ b/src/ui/panes/stacktrace.rs @@ -78,7 +78,7 @@ impl Pane for StackTracePane { } else { Span::styled( name, - Style::default().fg(Color::Cyan).bg(Color::Black).add_modifier(Modifier::BOLD), + Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD), ) }; parts.push(name); diff --git a/src/ui/state.rs b/src/ui/state.rs index a405476..e2c4508 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -667,9 +667,9 @@ impl State { /// Format the current debug variables as a string for display. /// - /// Returns a string describing all tracked variables and their current values, - /// or a message indicating no variables are being tracked. - pub fn format_variables(&self) -> String { + /// When `show_all` is false, compiler-generated locals (named `local0`, `local1`, etc.) + /// are hidden. Use `show_all` = true (`:vars all`) to include them. + pub fn format_variables(&self, show_all: bool) -> String { use core::fmt::Write; let executor = self.executor(); @@ -682,34 +682,41 @@ impl State { let mut output = String::new(); let stack = executor.current_stack.clone(); let context = executor.current_context; - let cycle = miden_processor::trace::RowIndex::from(executor.cycle as u32); - let execution_trace = match &self.session { - SessionState::Local(local) => Some(&local.execution_trace), - #[cfg(feature = "dap")] - SessionState::Remote(_) => None, + // Read memory from the live processor state (not the pre-recorded execution trace) + // so we get values at the current debugging cycle, not the final state. + let read_mem = |addr: u32| -> Option { + executor.processor.memory() + .read_element(context, Felt::new(addr as u64)) + .ok() }; for var_snapshot in debug_vars.current_variables() { + let name = var_snapshot.info.name(); + + // Skip compiler-generated locals (e.g. "local2") unless show_all is set. + // Source-level variables have DWARF-derived names (e.g. "a", "sum"). + if !show_all && is_compiler_generated_name(name) { + continue; + } + if !output.is_empty() { output.push_str(", "); } - let name = var_snapshot.info.name(); let location = var_snapshot.info.value_location(); // Try to resolve the variable value let value = resolve_variable_value( location, &stack, - |addr| { - execution_trace - .and_then(|t| t.read_memory_element_in_context(addr, context, cycle)) - }, - |_idx| { - // Local resolution would need FMP calculation - // For now, return None - None + |addr| read_mem(addr), + |offset| { + // Read FMP from live memory, then compute address as FMP + offset + let fmp_addr = miden_core::FMP_ADDR.as_canonical_u64() as u32; + let fmp = read_mem(fmp_addr)?; + let addr = (fmp.as_canonical_u64() as i64 + offset as i64) as u32; + read_mem(addr) }, ); @@ -723,10 +730,21 @@ impl State { } } - output + if output.is_empty() { + "No source-level variables (use ':vars all' to show compiler locals)".to_string() + } else { + output + } } } +/// Returns true if the variable name looks compiler-generated (e.g. "local0", "local12"). +/// Source-level variables have DWARF-derived names like "a", "sum", "_info". +fn is_compiler_generated_name(name: &str) -> bool { + name.strip_prefix("local") + .is_some_and(|suffix| !suffix.is_empty() && suffix.chars().all(|c| c.is_ascii_digit())) +} + // DAP CLIENT MODE // ================================================================================================ diff --git a/src/ui/syntax_highlighting.rs b/src/ui/syntax_highlighting.rs index 5cda390..187c5d0 100644 --- a/src/ui/syntax_highlighting.rs +++ b/src/ui/syntax_highlighting.rs @@ -254,14 +254,17 @@ impl HighlighterState for SyntectHighlighterState<'_> { /// Convert syntect [syntax::Style] into ratatui [Style] */ #[inline] pub fn convert_style(syntect_style: syntax::Style, use_bg_color: bool) -> Style { - let style = if use_bg_color { - let fg = blend_fg_color(syntect_style); - let bg = convert_color(syntect_style.background); - Style::new().fg(fg).bg(bg) - } else { - let fg = convert_color(syntect_style.foreground); - Style::new().fg(fg) - }; + let fg = syntect_style.foreground; + let bg = syntect_style.background; + let mut style = Style::new(); + // Skip transparent colors (alpha=0) to use the terminal's native colors + if fg.a > 0 { + let fg_color = if use_bg_color { blend_fg_color(syntect_style) } else { convert_color(fg) }; + style = style.fg(fg_color); + } + if use_bg_color && bg.a > 0 { + style = style.bg(convert_color(bg)); + } let mods = convert_font_style(syntect_style.font_style); style.add_modifier(mods) } @@ -270,9 +273,13 @@ pub fn convert_to_syntect_style(style: Style, _use_bg_color: bool) -> syntax::St let fg = style.fg.map(convert_to_syntect_color); let bg = style.bg.map(convert_to_syntect_color); let fs = convert_to_font_style(style.add_modifier); + // Use transparent (alpha=0) fallbacks so that convert_style will skip + // setting explicit colors, letting the terminal's native colors show through. + // This avoids hardcoded White/Black that break on light/dark terminals. + let transparent = syntax::Color { r: 0, g: 0, b: 0, a: 0 }; syntax::Style { - foreground: fg.unwrap_or(convert_to_syntect_color(Color::White)), - background: bg.unwrap_or(convert_to_syntect_color(Color::Black)), + foreground: fg.unwrap_or(transparent), + background: bg.unwrap_or(transparent), font_style: fs, } } From b913386488fbc5ee1192a5c52e29b1f90eb70f5a Mon Sep 17 00:00:00 2001 From: djole Date: Wed, 1 Apr 2026 11:47:03 +0200 Subject: [PATCH 05/12] feat: add REPL mode as alternative to TUI debugger --- Cargo.lock | 101 ++++++++++ Cargo.toml | 4 +- src/config.rs | 19 +- src/lib.rs | 12 +- src/main.rs | 17 +- src/repl/commands.rs | 144 +++++++++++++ src/repl/mod.rs | 16 ++ src/repl/session.rs | 470 +++++++++++++++++++++++++++++++++++++++++++ src/ui/mod.rs | 2 +- 9 files changed, 770 insertions(+), 15 deletions(-) create mode 100644 src/repl/commands.rs create mode 100644 src/repl/mod.rs create mode 100644 src/repl/session.rs diff --git a/Cargo.lock b/Cargo.lock index 8f4747e..ae89c19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,6 +232,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.9.1" @@ -317,6 +323,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -691,6 +706,12 @@ dependencies = [ "log", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "env_filter" version = "1.0.1" @@ -730,6 +751,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "fake" version = "2.10.0" @@ -747,6 +774,17 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix 1.1.4", + "windows-sys 0.59.0", +] + [[package]] name = "ff" version = "0.13.1" @@ -1031,6 +1069,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -1467,6 +1514,7 @@ dependencies = [ "proptest", "ratatui", "rustc-demangle", + "rustyline", "serde", "serde_json", "signal-hook", @@ -1850,6 +1898,27 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2477,6 +2546,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -2728,6 +2807,28 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "rustyline" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width 0.2.0", + "utf8parse", + "windows-sys 0.59.0", +] + [[package]] name = "ryu" version = "1.0.23" diff --git a/Cargo.toml b/Cargo.toml index c8f8871..25f93d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ name = "miden-debug" path = "src/main.rs" test = false bench = false -required-features = ["tui"] +required-features = ["std"] [[example]] name = "compile-masm" @@ -34,6 +34,7 @@ required-features = ["std"] [features] default = ["tui", "dap"] tui = ["std", "dep:crossterm", "dep:env_logger", "dep:ratatui", "dep:tui-input", "dep:signal-hook", "dep:syntect", "miden-debug-engine/tui"] +repl = ["std", "dep:env_logger", "dep:rustyline", "miden-debug-engine/tui"] dap = ["dep:dap", "dep:socket2", "miden-debug-engine/dap"] std = ["dep:glob", "clap/std", "clap/env", "miden-assembly-syntax/std", "miden-debug-engine/std"] proptest = ["dep:proptest", "miden-debug-engine/proptest"] @@ -80,6 +81,7 @@ syntect = { version = "5.2.0", optional = true, default-features = false, featur thiserror = { package = "miden-thiserror", version = "1.0" } toml = { version = "0.8", features = ["preserve_order"] } tui-input = { version = "0.11", optional = true } +rustyline = { version = "15.0", optional = true } socket2 = { version = "0.5", optional = true } tokio = { version = "1.39.2", features = ["rt", "time", "macros", "rt-multi-thread"] } diff --git a/src/config.rs b/src/config.rs index 99da4ee..cd8c478 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,22 +8,22 @@ use crate::{exec::ExecutionConfig, felt::Felt, input::InputFile, linker::LinkLib /// Run a compiled Miden program with the Miden VM #[derive(Default, Debug)] -#[cfg_attr(feature = "tui", derive(clap::Parser))] -#[cfg_attr(feature = "tui", command(author, version, about = "The interactive Miden debugger", long_about = None))] +#[cfg_attr(any(feature = "tui", feature = "repl"), derive(clap::Parser))] +#[cfg_attr(any(feature = "tui", feature = "repl"), command(author, version, about = "The interactive Miden debugger", long_about = None))] pub struct DebuggerConfig { /// Specify the path to a Miden program file to execute. /// /// Miden Assembly programs are emitted by the compiler with a `.masp` extension. /// /// You may use `-` as a file name to read a file from stdin. - #[cfg_attr(feature = "tui", arg(value_name = "FILE"))] + #[cfg_attr(any(feature = "tui", feature = "repl"), arg(value_name = "FILE"))] pub input: Option, /// Specify the path to a file containing program inputs. /// /// Program inputs are stack and advice provider values which the program can /// access during execution. The inputs file is a TOML file which describes /// what the inputs are, or where to source them from. - #[cfg_attr(feature = "tui", arg(long, value_name = "FILE"))] + #[cfg_attr(any(feature = "tui", feature = "repl"), arg(long, value_name = "FILE"))] pub inputs: Option, /// Arguments to place on the operand stack before calling the program entrypoint. /// @@ -34,7 +34,7 @@ pub struct DebuggerConfig { /// These arguments must be valid field element values expressed in decimal format. /// /// NOTE: These arguments will override any stack values provided via --inputs - #[cfg_attr(feature = "tui", arg(last(true), value_name = "ARGV"))] + #[cfg_attr(any(feature = "tui", feature = "repl"), arg(last(true), value_name = "ARGV"))] pub args: Vec, /// The working directory for the debugger /// @@ -58,7 +58,7 @@ pub struct DebuggerConfig { )] pub sysroot: Option, /// Whether, and how, to color terminal output - #[cfg_attr(feature = "tui", arg( + #[cfg_attr(any(feature = "tui", feature = "repl"), arg( long, value_enum, default_value_t = ColorChoice::Auto, @@ -69,7 +69,7 @@ pub struct DebuggerConfig { pub color: ColorChoice, /// Specify the function to call as the entrypoint for the program /// in the format `::` - #[cfg_attr(feature = "tui", arg(long, help_heading = "Execution"))] + #[cfg_attr(any(feature = "tui", feature = "repl"), arg(long, help_heading = "Execution"))] pub entrypoint: Option, /// Connect to a remote DAP debug server instead of running a local program. /// @@ -114,6 +114,9 @@ pub struct DebuggerConfig { ) )] pub link_libraries: Vec, + /// Use the REPL (text-mode) debugger instead of the TUI + #[cfg_attr(any(feature = "tui", feature = "repl"), arg(long, help_heading = "Output"))] + pub repl: bool, } /// ColorChoice represents the color preferences of an end user. @@ -125,7 +128,7 @@ pub struct DebuggerConfig { /// string of the variant name to the corresponding variant. Any other string /// results in an error. #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "tui", derive(clap::ValueEnum))] +#[cfg_attr(any(feature = "tui", feature = "repl"), derive(clap::ValueEnum))] pub enum ColorChoice { /// Try very hard to emit colors. This includes emitting ANSI colors /// on Windows if the console API is unavailable. diff --git a/src/lib.rs b/src/lib.rs index 84f88ce..cb5205d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,13 +4,19 @@ mod config; mod input; mod linker; -#[cfg(feature = "tui")] +#[cfg(any(feature = "tui", feature = "repl"))] mod logger; -#[cfg(feature = "tui")] -mod ui; +#[cfg(any(feature = "tui", feature = "repl"))] +pub(crate) mod ui; + +#[cfg(feature = "repl")] +mod repl; #[cfg(feature = "tui")] pub use self::ui::{DebugMode, State, run, run_with_state}; + +#[cfg(feature = "repl")] +pub use self::repl::run as run_repl; pub use self::{ config::{ColorChoice, DebuggerConfig}, debug::*, diff --git a/src/main.rs b/src/main.rs index dc2f633..2b08459 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,11 @@ use std::env; use clap::Parser; use miden_assembly_syntax::diagnostics::{IntoDiagnostic, Report, WrapErr}; -use miden_debug::{DebuggerConfig, run}; +use miden_debug::DebuggerConfig; +#[cfg(feature = "tui")] +use miden_debug::run; +#[cfg(feature = "repl")] +use miden_debug::run_repl; #[cfg(feature = "dap")] use miden_debug::{State, run_with_state}; @@ -46,7 +50,16 @@ pub fn main() -> Result<(), Report> { return run_with_state(state, logger); } - run(config, logger) + #[cfg(feature = "repl")] + if config.repl { + return run_repl(config, logger); + } + + #[cfg(feature = "tui")] + return run(config, logger); + + #[cfg(not(feature = "tui"))] + Err(Report::msg("no UI feature enabled: build with --features tui or --features repl")) } fn setup_diagnostics() { diff --git a/src/repl/commands.rs b/src/repl/commands.rs new file mode 100644 index 0000000..c05f6a2 --- /dev/null +++ b/src/repl/commands.rs @@ -0,0 +1,144 @@ +use std::str::FromStr; + +use crate::debug::{BreakpointType, ReadMemoryExpr}; + +/// Commands available in the REPL debugger. +#[derive(Debug, Clone)] +pub enum ReplCommand { + /// Execute one VM cycle + Step, + /// Execute N VM cycles + StepN(usize), + /// Execute until next instruction boundary + Next, + /// Run until breakpoint or end + Continue, + /// Run until current function returns + Finish, + /// Set a breakpoint + Break(BreakpointType), + /// List all breakpoints + Breakpoints, + /// Delete breakpoint(s) - None means delete all + Delete(Option), + /// Show operand stack + Stack, + /// Show memory at address with optional count + Memory(ReadMemoryExpr), + /// Show local variables + Locals, + /// Show all debug variables + Vars, + /// Show current source location + Where, + /// Show recent instructions + List, + /// Show call stack / backtrace + Backtrace, + /// Restart program + Reload, + /// Show help + Help, + /// Exit debugger + Quit, +} + +impl FromStr for ReplCommand { + type Err = String; + + fn from_str(s: &str) -> Result { + let s = s.trim(); + if s.is_empty() { + return Err("empty command".into()); + } + + // Split into command and arguments + let (cmd, args) = match s.split_once(char::is_whitespace) { + Some((cmd, args)) => (cmd, Some(args.trim())), + None => (s, None), + }; + + match cmd { + // Step commands + "s" | "step" => match args { + Some(n) => { + let n = n.parse::().map_err(|e| format!("invalid step count: {e}"))?; + Ok(ReplCommand::StepN(n)) + } + None => Ok(ReplCommand::Step), + }, + "n" | "next" => Ok(ReplCommand::Next), + "c" | "continue" => Ok(ReplCommand::Continue), + "e" | "finish" => Ok(ReplCommand::Finish), + + // Breakpoint commands + "b" | "break" | "breakpoint" => { + let args = args.ok_or("breakpoint requires a specification")?; + let bp_type = args.parse::()?; + Ok(ReplCommand::Break(bp_type)) + } + "bp" | "breakpoints" => Ok(ReplCommand::Breakpoints), + "d" | "delete" => match args { + Some(id) => { + let id = id.parse::().map_err(|e| format!("invalid breakpoint id: {e}"))?; + Ok(ReplCommand::Delete(Some(id))) + } + None => Ok(ReplCommand::Delete(None)), + }, + + // Inspection commands + "stack" => Ok(ReplCommand::Stack), + "mem" | "memory" => { + let args = args.ok_or("memory command requires an address")?; + let expr = args.parse::()?; + Ok(ReplCommand::Memory(expr)) + } + "locals" => Ok(ReplCommand::Locals), + "vars" | "variables" => Ok(ReplCommand::Vars), + "where" | "w" => Ok(ReplCommand::Where), + "l" | "list" => Ok(ReplCommand::List), + "bt" | "backtrace" => Ok(ReplCommand::Backtrace), + + // Control commands + "reload" => Ok(ReplCommand::Reload), + "h" | "help" | "?" => Ok(ReplCommand::Help), + "q" | "quit" | "exit" => Ok(ReplCommand::Quit), + + _ => Err(format!("unknown command: {cmd}")), + } + } +} + +impl ReplCommand { + /// Returns the help text for all commands. + pub fn help_text() -> &'static str { + r#"Available commands: + +Execution: + s, step [N] Execute one (or N) VM cycle(s) + n, next Execute until next instruction boundary + c, continue Run until breakpoint or end + e, finish Run until current function returns + reload Restart program execution + +Breakpoints: + b, break Set a breakpoint + Specs: at , after , in , :, + bp, breakpoints List all breakpoints + d, delete [id] Delete breakpoint by id, or all if no id given + +Inspection: + stack Show operand stack + mem [type] Show memory at address (e.g., mem 0x100 u32) + locals Show local variables + vars Show all debug variables + where Show current source location + l, list Show recent instructions + bt, backtrace Show call stack + +Other: + h, help Show this help + q, quit Exit debugger +"# + } +} diff --git a/src/repl/mod.rs b/src/repl/mod.rs new file mode 100644 index 0000000..7fb14dd --- /dev/null +++ b/src/repl/mod.rs @@ -0,0 +1,16 @@ +mod commands; +mod session; + +use miden_assembly_syntax::diagnostics::Report; + +use self::session::ReplSession; +use crate::config::DebuggerConfig; + +/// Run the REPL debugger with the given configuration. +pub fn run(config: Box, logger: Box) -> Result<(), Report> { + // Install the logger + crate::logger::DebugLogger::install(logger); + + let mut session = ReplSession::new(config)?; + session.run() +} diff --git a/src/repl/session.rs b/src/repl/session.rs new file mode 100644 index 0000000..1b6bb4e --- /dev/null +++ b/src/repl/session.rs @@ -0,0 +1,470 @@ +use std::rc::Rc; + +use miden_assembly_syntax::diagnostics::Report; +use rustyline::{DefaultEditor, error::ReadlineError}; + +use super::commands::ReplCommand; +use crate::{ + config::DebuggerConfig, + debug::{Breakpoint, BreakpointType}, + ui::state::State, +}; + +/// Interactive REPL session for the debugger. +pub struct ReplSession { + state: State, + editor: DefaultEditor, +} + +impl ReplSession { + /// Create a new REPL session from the given config. + pub fn new(config: Box) -> Result { + let state = State::new(config)?; + let editor = DefaultEditor::new() + .map_err(|e| Report::msg(format!("failed to create editor: {e}")))?; + + Ok(Self { state, editor }) + } + + /// Run the main REPL loop. + pub fn run(&mut self) -> Result<(), Report> { + self.print_welcome(); + self.print_location(); + + loop { + let prompt = self.make_prompt(); + match self.editor.readline(&prompt) { + Ok(line) => { + let line = line.trim(); + if line.is_empty() { + continue; + } + + // Add to history + let _ = self.editor.add_history_entry(line); + + // Parse and execute command + match line.parse::() { + Ok(cmd) => { + if matches!(cmd, ReplCommand::Quit) { + println!("\x1b[36mGoodbye!\x1b[0m"); + break; + } + if let Err(e) = self.execute_command(cmd) { + eprintln!("\x1b[31mError:\x1b[0m {e}"); + } + } + Err(e) => { + eprintln!("\x1b[31mError:\x1b[0m {e}"); + } + } + } + Err(ReadlineError::Interrupted) => { + println!("^C"); + continue; + } + Err(ReadlineError::Eof) => { + println!("\x1b[36mGoodbye!\x1b[0m"); + break; + } + Err(e) => { + eprintln!("\x1b[31mError reading line:\x1b[0m {e}"); + break; + } + } + } + + Ok(()) + } + + fn print_welcome(&self) { + println!("\x1b[1;36mMiden Debugger REPL\x1b[0m"); + println!("Type \x1b[33mhelp\x1b[0m for available commands."); + println!(); + } + + fn make_prompt(&self) -> String { + let cycle = self.state.executor().cycle; + + if self.state.executor().stopped { + if self.state.execution_failed().is_some() { + format!("\x1b[36m[\x1b[0mcycle {cycle} \x1b[1;31mERR\x1b[0m\x1b[36m]\x1b[0m > ") + } else { + format!("\x1b[36m[\x1b[0mcycle {cycle} \x1b[1;32mEND\x1b[0m\x1b[36m]\x1b[0m > ") + } + } else if self.state.stopped { + format!("\x1b[36m[\x1b[0mcycle {cycle} \x1b[1;33mSTOP\x1b[0m\x1b[36m]\x1b[0m > ") + } else { + format!("\x1b[36m[\x1b[0mcycle {cycle}\x1b[36m]\x1b[0m > ") + } + } + + fn print_location(&self) { + if let Some(frame) = self.state.executor().callstack.current_frame() { + if let Some(resolved) = frame.last_resolved(&*self.state.source_manager) { + let proc_name = frame.procedure("").unwrap_or_else(|| Rc::from("")); + println!("at {} in {}", resolved, proc_name); + } else if let Some(proc_name) = frame.procedure("") { + println!("in {}", proc_name); + } + } + } + + fn execute_command(&mut self, cmd: ReplCommand) -> Result<(), String> { + match cmd { + ReplCommand::Step => self.cmd_step(1), + ReplCommand::StepN(n) => self.cmd_step(n), + ReplCommand::Next => self.cmd_next(), + ReplCommand::Continue => self.cmd_continue(), + ReplCommand::Finish => self.cmd_finish(), + ReplCommand::Break(bp_type) => self.cmd_break(bp_type), + ReplCommand::Breakpoints => self.cmd_breakpoints(), + ReplCommand::Delete(id) => self.cmd_delete(id), + ReplCommand::Stack => self.cmd_stack(), + ReplCommand::Memory(expr) => self.cmd_memory(&expr), + ReplCommand::Locals => self.cmd_locals(), + ReplCommand::Vars => self.cmd_vars(), + ReplCommand::Where => self.cmd_where(), + ReplCommand::List => self.cmd_list(), + ReplCommand::Backtrace => self.cmd_backtrace(), + ReplCommand::Reload => self.cmd_reload(), + ReplCommand::Help => self.cmd_help(), + ReplCommand::Quit => unreachable!("quit handled in run loop"), + } + } + + fn cmd_step(&mut self, n: usize) -> Result<(), String> { + if self.state.executor().stopped { + return Err("program has terminated, cannot step".into()); + } + + for _ in 0..n { + if self.state.executor().stopped { + break; + } + match self.state.executor_mut().step() { + Ok(_) => {} + Err(err) => { + let msg = format!("execution error: {err}"); + self.state.set_execution_failed(err); + return Err(msg); + } + } + } + + self.print_location(); + Ok(()) + } + + fn cmd_next(&mut self) -> Result<(), String> { + if self.state.executor().stopped { + return Err("program has terminated, cannot continue".into()); + } + + self.state.create_breakpoint(BreakpointType::Next); + self.state.stopped = false; + self.run_until_stopped(); + self.print_location(); + Ok(()) + } + + fn cmd_continue(&mut self) -> Result<(), String> { + if self.state.executor().stopped { + return Err("program has terminated, cannot continue".into()); + } + + self.state.stopped = false; + self.run_until_stopped(); + + if self.state.executor().stopped { + if let Some(err) = self.state.execution_failed() { + println!("Program terminated with error: {}", err); + } else { + println!("Program terminated successfully"); + } + } else { + self.print_location(); + } + + Ok(()) + } + + fn cmd_finish(&mut self) -> Result<(), String> { + if self.state.executor().stopped { + return Err("program has terminated, cannot continue".into()); + } + + self.state.create_breakpoint(BreakpointType::Finish); + self.state.stopped = false; + self.run_until_stopped(); + self.print_location(); + Ok(()) + } + + fn run_until_stopped(&mut self) { + let start_cycle = self.state.executor().cycle; + let mut breakpoints: Vec = core::mem::take(&mut self.state.breakpoints); + + loop { + // Check if program has terminated + if self.state.executor().stopped { + self.state.stopped = true; + break; + } + + let mut consume_most_recent_finish = false; + match self.state.executor_mut().step() { + Ok(Some(ref exited)) if exited.should_break_on_exit() => { + consume_most_recent_finish = true; + } + Ok(_) => {} + Err(err) => { + self.state.set_execution_failed(err); + self.state.stopped = true; + break; + } + } + + if breakpoints.is_empty() { + continue; + } + + // Get current execution state for breakpoint checking + let is_op_boundary = + self.state.executor().current_asmop.as_ref().map(|_info| true).unwrap_or(false); + let (proc, loc) = match self.state.executor().callstack.current_frame() { + Some(frame) => { + let loc = frame + .recent() + .back() + .and_then(|detail| detail.resolve(&*self.state.source_manager)) + .cloned(); + (frame.procedure(""), loc) + } + None => (None, None), + }; + + // Check breakpoints + let current_cycle = self.state.executor().cycle; + let cycles_stepped = current_cycle - start_cycle; + + breakpoints.retain_mut(|bp| { + if let Some(n) = bp.cycles_to_skip(current_cycle) { + if cycles_stepped >= n { + let retained = !bp.is_one_shot(); + if retained { + self.state.breakpoints_hit.push(bp.clone()); + } else { + self.state.breakpoints_hit.push(core::mem::take(bp)); + } + return retained; + } + return true; + } + + if cycles_stepped > 0 && is_op_boundary && matches!(&bp.ty, BreakpointType::Next) { + self.state.breakpoints_hit.push(core::mem::take(bp)); + return false; + } + + if let Some(loc) = loc.as_ref() + && bp.should_break_at(loc) + { + let retained = !bp.is_one_shot(); + if retained { + self.state.breakpoints_hit.push(bp.clone()); + } else { + self.state.breakpoints_hit.push(core::mem::take(bp)); + } + return retained; + } + + if let Some(proc) = proc.as_deref() + && bp.should_break_in(proc) + { + let retained = !bp.is_one_shot(); + if retained { + self.state.breakpoints_hit.push(bp.clone()); + } else { + self.state.breakpoints_hit.push(core::mem::take(bp)); + } + return retained; + } + + true + }); + + // Handle Finish breakpoint + if consume_most_recent_finish + && let Some(id) = breakpoints.iter().rev().find_map(|bp| { + if matches!(bp.ty, BreakpointType::Finish) { + Some(bp.id) + } else { + None + } + }) + { + breakpoints.retain(|bp| bp.id != id); + self.state.stopped = true; + break; + } + + if !self.state.breakpoints_hit.is_empty() { + self.state.stopped = true; + break; + } + } + + // Restore breakpoints + self.state.breakpoints = breakpoints; + } + + fn cmd_break(&mut self, bp_type: BreakpointType) -> Result<(), String> { + self.state.create_breakpoint(bp_type.clone()); + let id = self.state.breakpoints.last().map(|bp| bp.id).unwrap_or(0); + println!("Breakpoint {} created: {:?}", id, bp_type); + Ok(()) + } + + fn cmd_breakpoints(&mut self) -> Result<(), String> { + if self.state.breakpoints.is_empty() { + println!("No breakpoints set"); + return Ok(()); + } + + println!("Breakpoints:"); + for bp in &self.state.breakpoints { + if !bp.is_internal() { + println!(" [{}] {:?}", bp.id, bp.ty); + } + } + Ok(()) + } + + fn cmd_delete(&mut self, id: Option) -> Result<(), String> { + match id { + Some(id) => { + let count_before = self.state.breakpoints.len(); + self.state.breakpoints.retain(|bp| bp.id != id); + if self.state.breakpoints.len() < count_before { + println!("Deleted breakpoint {}", id); + } else { + return Err(format!("no breakpoint with id {}", id)); + } + } + None => { + // Delete only user-created (non-internal) breakpoints + self.state.breakpoints.retain(|bp| bp.is_internal()); + println!("Deleted all breakpoints"); + } + } + Ok(()) + } + + fn cmd_stack(&mut self) -> Result<(), String> { + let stack = &self.state.executor().current_stack; + + if stack.is_empty() { + println!("Stack is empty"); + return Ok(()); + } + + println!("Operand Stack ({} elements):", stack.len()); + for (i, elem) in stack.iter().enumerate() { + let val = elem.as_canonical_u64(); + let marker = if i == 0 { ">" } else { " " }; + println!(" {} [{}] {} (0x{:x})", marker, i, val, val); + } + Ok(()) + } + + fn cmd_memory(&mut self, expr: &crate::debug::ReadMemoryExpr) -> Result<(), String> { + let result = self.state.read_memory(expr)?; + println!("{}", result); + Ok(()) + } + + fn cmd_locals(&mut self) -> Result<(), String> { + let output = self.state.format_variables(false); + println!("{}", output); + Ok(()) + } + + fn cmd_vars(&mut self) -> Result<(), String> { + let output = self.state.format_variables(false); + println!("{}", output); + Ok(()) + } + + fn cmd_where(&mut self) -> Result<(), String> { + if let Some(frame) = self.state.executor().callstack.current_frame() { + let proc_name = frame.procedure("").unwrap_or_else(|| Rc::from("")); + + if let Some(resolved) = frame.last_resolved(&*self.state.source_manager) { + println!( + "{}:{}:{} in {}", + resolved.source_file.uri().as_str(), + resolved.line, + resolved.col, + proc_name + ); + } else { + println!("in {} (no source location available)", proc_name); + } + } else { + println!("No current frame"); + } + Ok(()) + } + + fn cmd_list(&mut self) -> Result<(), String> { + if let Some(frame) = self.state.executor().callstack.current_frame() { + let recent = frame.recent(); + if recent.is_empty() { + println!("No recent instructions"); + return Ok(()); + } + + println!("Recent instructions:"); + for (i, op) in recent.iter().enumerate() { + let marker = if i == recent.len() - 1 { ">" } else { " " }; + println!(" {} {}", marker, op.display()); + } + } else { + println!("No current frame"); + } + Ok(()) + } + + fn cmd_backtrace(&mut self) -> Result<(), String> { + let frames = self.state.executor().callstack.frames(); + if frames.is_empty() { + println!("No call stack"); + return Ok(()); + } + + println!("Backtrace ({} frames):", frames.len()); + for (i, frame) in frames.iter().rev().enumerate() { + let proc_name = frame.procedure("").unwrap_or_else(|| Rc::from("")); + let loc_str = frame + .last_resolved(&*self.state.source_manager) + .map(|r| format!(" at {}", r)) + .unwrap_or_default(); + + println!(" #{} {}{}", i, proc_name, loc_str); + } + Ok(()) + } + + fn cmd_reload(&mut self) -> Result<(), String> { + self.state.reload().map_err(|e| format!("reload failed: {e}"))?; + println!("Program reloaded"); + self.print_location(); + Ok(()) + } + + fn cmd_help(&mut self) -> Result<(), String> { + println!("{}", ReplCommand::help_text()); + Ok(()) + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 568eb63..5453442 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -3,7 +3,7 @@ mod app; mod duration; mod pages; mod panes; -mod state; +pub(crate) mod state; mod syntax_highlighting; mod tui; From 4a5473cf0b2d6e99aadce3db27779a14ace2ae6c Mon Sep 17 00:00:00 2001 From: djole Date: Wed, 1 Apr 2026 11:51:40 +0200 Subject: [PATCH 06/12] fix: use human-readable breakpoint display in REPL --- src/repl/session.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/repl/session.rs b/src/repl/session.rs index 1b6bb4e..79b5023 100644 --- a/src/repl/session.rs +++ b/src/repl/session.rs @@ -322,7 +322,7 @@ impl ReplSession { fn cmd_break(&mut self, bp_type: BreakpointType) -> Result<(), String> { self.state.create_breakpoint(bp_type.clone()); let id = self.state.breakpoints.last().map(|bp| bp.id).unwrap_or(0); - println!("Breakpoint {} created: {:?}", id, bp_type); + println!("Breakpoint {} set: {}", id, format_bp_type(&bp_type)); Ok(()) } @@ -335,7 +335,7 @@ impl ReplSession { println!("Breakpoints:"); for bp in &self.state.breakpoints { if !bp.is_internal() { - println!(" [{}] {:?}", bp.id, bp.ty); + println!(" [{}] {}", bp.id, format_bp_type(&bp.ty)); } } Ok(()) @@ -468,3 +468,17 @@ impl ReplSession { Ok(()) } } + +fn format_bp_type(ty: &BreakpointType) -> String { + match ty { + BreakpointType::Step => "next cycle".into(), + BreakpointType::StepN(n) => format!("after {} cycles", n), + BreakpointType::StepTo(c) => format!("at cycle {}", c), + BreakpointType::Next => "next instruction".into(), + BreakpointType::Finish => "function return".into(), + BreakpointType::File(pat) => format!("{}", pat.as_str()), + BreakpointType::Line { pattern, line } => format!("{}:{}", pattern.as_str(), line), + BreakpointType::Opcode(op) => format!("opcode {:?}", op), + BreakpointType::Called(pat) => format!("call {}", pat.as_str()), + } +} From 88c5f0557fa68195bbce60e27608fa0548170f52 Mon Sep 17 00:00:00 2001 From: djole Date: Wed, 1 Apr 2026 12:13:24 +0200 Subject: [PATCH 07/12] fixup: cleanup --- crates/engine/src/debug/variables.rs | 15 ++++----------- src/repl/session.rs | 3 +-- src/ui/state.rs | 6 +----- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index f8dffe9..7a3d2e6 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -12,11 +12,7 @@ pub struct DebugVarSnapshot { pub info: DebugVarInfo, } -/// Tracks debug variable information throughout program execution. -/// -/// This structure maintains a mapping from variable names to their most recent -/// location information at each clock cycle. It's designed to work with the -/// debugger to provide source-level variable inspection. +/// Tracks debug variable snapshots, mapping variable names to their most recent location info. pub struct DebugVarTracker { /// All debug variable events recorded during execution, keyed by clock cycle. events: Rc>>>, @@ -43,10 +39,7 @@ impl DebugVarTracker { } } - /// Update the tracker state to reflect variables at the given clock cycle. - /// - /// This processes all events up to and including `clk`, updating the - /// current variable state accordingly. + /// Process all events up to and including `clk`, updating current variable state. pub fn update_to_cycle(&mut self, clk: RowIndex) { let events = self.events.borrow(); @@ -96,13 +89,13 @@ pub fn resolve_variable_value( location: &DebugVarLocation, stack: &[Felt], get_memory: impl Fn(u32) -> Option, - _get_local: impl Fn(i16) -> Option, + get_local: impl Fn(i16) -> Option, ) -> Option { match location { DebugVarLocation::Stack(pos) => stack.get(*pos as usize).copied(), DebugVarLocation::Memory(addr) => get_memory(*addr), DebugVarLocation::Const(felt) => Some(*felt), - DebugVarLocation::Local(offset) => _get_local(*offset), + DebugVarLocation::Local(offset) => get_local(*offset), DebugVarLocation::FrameBase { global_index, byte_offset } => { // global_index was resolved to a Miden byte address during compilation. // Convert to element address (÷4) to read the stack pointer value. diff --git a/src/repl/session.rs b/src/repl/session.rs index 79b5023..713e483 100644 --- a/src/repl/session.rs +++ b/src/repl/session.rs @@ -230,8 +230,7 @@ impl ReplSession { } // Get current execution state for breakpoint checking - let is_op_boundary = - self.state.executor().current_asmop.as_ref().map(|_info| true).unwrap_or(false); + let is_op_boundary = self.state.executor().current_asmop.is_some(); let (proc, loc) = match self.state.executor().callstack.current_frame() { Some(frame) => { let loc = frame diff --git a/src/ui/state.rs b/src/ui/state.rs index e2c4508..44796ce 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -683,8 +683,7 @@ impl State { let stack = executor.current_stack.clone(); let context = executor.current_context; - // Read memory from the live processor state (not the pre-recorded execution trace) - // so we get values at the current debugging cycle, not the final state. + // Use live processor state, not the pre-recorded trace, for current-cycle values. let read_mem = |addr: u32| -> Option { executor.processor.memory() .read_element(context, Felt::new(addr as u64)) @@ -694,8 +693,6 @@ impl State { for var_snapshot in debug_vars.current_variables() { let name = var_snapshot.info.name(); - // Skip compiler-generated locals (e.g. "local2") unless show_all is set. - // Source-level variables have DWARF-derived names (e.g. "a", "sum"). if !show_all && is_compiler_generated_name(name) { continue; } @@ -706,7 +703,6 @@ impl State { let location = var_snapshot.info.value_location(); - // Try to resolve the variable value let value = resolve_variable_value( location, &stack, From ab1af0f697aa1723531980b94665b6c403644577 Mon Sep 17 00:00:00 2001 From: djole Date: Wed, 1 Apr 2026 14:54:40 +0200 Subject: [PATCH 08/12] chore: fix formatting after rebase --- Cargo.lock | 453 +++++++++++++++++---------- crates/engine/src/debug/variables.rs | 10 +- crates/engine/src/exec/executor.rs | 8 +- src/config.rs | 15 +- src/lib.rs | 5 +- src/main.rs | 4 +- src/ui/panes/source_code.rs | 10 +- src/ui/panes/stacktrace.rs | 5 +- src/ui/state.rs | 28 +- src/ui/syntax_highlighting.rs | 13 +- 10 files changed, 346 insertions(+), 205 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae89c19..8f69afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,22 +160,22 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", ] [[package]] @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.57" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -257,7 +257,7 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -741,6 +741,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.14" @@ -770,9 +781,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fd-lock" @@ -1006,7 +1017,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "rand_core 0.10.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -1041,9 +1052,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" @@ -1098,12 +1109,12 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -1205,9 +1216,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ "once_cell", "wasm-bindgen", @@ -1280,9 +1291,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libm" @@ -1362,7 +1373,21 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miden-air" -version = "0.22.0" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15646ebc95906b2a7cb66711d1e184f53fd6edc2605730bbcf0c2a129f792cf" +dependencies = [ + "miden-core", + "miden-crypto", + "miden-utils-indexing", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "miden-air" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "miden-core", "miden-crypto", @@ -1373,19 +1398,23 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "log", "miden-assembly-syntax", "miden-core", "miden-mast-package", + "miden-package-registry", + "miden-project", "smallvec", "thiserror 2.0.18", ] [[package]] name = "miden-assembly-syntax" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "aho-corasick", "lalrpop", @@ -1398,14 +1427,16 @@ dependencies = [ "proptest", "regex", "rustc_version 0.4.1", - "semver 1.0.27", + "semver 1.0.28", + "serde", "smallvec", "thiserror 2.0.18", ] [[package]] name = "miden-core" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "derive_more", "itertools 0.14.0", @@ -1414,25 +1445,26 @@ dependencies = [ "miden-formatting", "miden-utils-core-derive", "miden-utils-indexing", - "miden-utils-sync 0.22.0", + "miden-utils-sync", "num-derive", "num-traits", + "serde", "thiserror 2.0.18", ] [[package]] name = "miden-core-lib" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82595fabb062315c32f6fc11c31755d3e5c6f8bc8c67d35154a067397d65b1de" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "env_logger", "fs-err", "miden-assembly", "miden-core", "miden-crypto", + "miden-package-registry", "miden-processor", - "miden-utils-sync 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "miden-utils-sync", "thiserror 2.0.18", ] @@ -1466,7 +1498,7 @@ dependencies = [ "p3-miden-lifted-stark", "p3-symmetric", "p3-util", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_core 0.9.5", "rand_hc", @@ -1532,7 +1564,7 @@ name = "miden-debug-dap" version = "0.1.0" dependencies = [ "fake", - "rand 0.10.0", + "rand 0.10.1", "serde", "serde_json", "thiserror 1.0.69", @@ -1566,17 +1598,18 @@ dependencies = [ [[package]] name = "miden-debug-types" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "memchr", "miden-crypto", "miden-formatting", "miden-miette", "miden-utils-indexing", - "miden-utils-sync 0.22.0", + "miden-utils-sync", "paste", "serde", - "serde_spanned 1.1.0", + "serde_spanned 1.1.1", "thiserror 2.0.18", ] @@ -1592,7 +1625,7 @@ dependencies = [ "p3-field", "p3-goldilocks", "paste", - "rand 0.10.0", + "rand 0.10.1", "serde", "subtle", "thiserror 2.0.18", @@ -1609,12 +1642,14 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "derive_more", "miden-assembly-syntax", "miden-core", "miden-debug-types", + "serde", "thiserror 2.0.18", ] @@ -1654,12 +1689,27 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "miden-package-registry" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" +dependencies = [ + "miden-assembly-syntax", + "miden-core", + "miden-mast-package", + "pubgrub", + "serde", + "smallvec", + "thiserror 2.0.18", +] + [[package]] name = "miden-processor" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "itertools 0.14.0", - "miden-air", + "miden-air 0.22.1 (git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash)", "miden-core", "miden-debug-types", "miden-utils-diagnostics", @@ -1671,10 +1721,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "miden-project" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" +dependencies = [ + "miden-assembly-syntax", + "miden-core", + "miden-mast-package", + "miden-package-registry", + "serde", + "serde-untagged", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", +] + [[package]] name = "miden-protocol" -version = "0.14.0" -source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e860cc978d3467297de076e9bd22f0573b82ef73a3d223d6bb957731a45b8164" dependencies = [ "bech32", "fs-err", @@ -1687,19 +1753,20 @@ dependencies = [ "miden-mast-package", "miden-processor", "miden-protocol-macros", - "miden-utils-sync 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "miden-utils-sync", "miden-verifier", - "rand 0.9.2", + "rand 0.9.4", "regex", - "semver 1.0.27", + "semver 1.0.28", "thiserror 2.0.18", "walkdir", ] [[package]] name = "miden-protocol-macros" -version = "0.14.0" -source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4daec4a5a6f050a670a8639e78e017ab11ef0bf2e253b012505f25e6247c13e7" dependencies = [ "proc-macro2", "quote", @@ -1708,12 +1775,12 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15462425359e87540d92e277cf1174a85a174ca433bd63d27286f65ab318f2d4" +checksum = "6bb2c94e36f57684d7fa0cd382adeedc1728d502dbbe69ad1c12f4a931f45511" dependencies = [ "bincode", - "miden-air", + "miden-air 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", "miden-core", "miden-crypto", "miden-debug-types", @@ -1736,8 +1803,9 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.14.0" -source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f455a087f41c30636b45ead961d1e66114d2d20661887b307cede05307eeb942" dependencies = [ "fs-err", "miden-assembly", @@ -1772,8 +1840,9 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.14.0" -source = "git+https://github.com/0xMiden/protocol?rev=ae4b45778c7e2c73be87b67906b7fa1ae8e2a285#ae4b45778c7e2c73be87b67906b7fa1ae8e2a285" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d788795041ce5e6f947a3256314373171e4877c11b86fafeabcec4d8b8628d9" dependencies = [ "miden-processor", "miden-protocol", @@ -1785,7 +1854,8 @@ dependencies = [ [[package]] name = "miden-utils-core-derive" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "proc-macro2", "quote", @@ -1794,7 +1864,8 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "miden-crypto", "miden-debug-types", @@ -1805,15 +1876,18 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "miden-crypto", + "serde", "thiserror 2.0.18", ] [[package]] name = "miden-utils-sync" -version = "0.22.0" +version = "0.22.1" +source = "git+https://github.com/walnuthq/miden-vm?branch=fix%2Fdebug-var-dedup-crash#0175cffa90e7573f36edbd04ef8ba261b440041b" dependencies = [ "lock_api", "loom", @@ -1821,25 +1895,14 @@ dependencies = [ "parking_lot", ] -[[package]] -name = "miden-utils-sync" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529c1c173506f30d3949f7a54b65f1eb318098e37ed5730a1bb9027eee2fa4b" -dependencies = [ - "lock_api", - "loom", - "once_cell", -] - [[package]] name = "miden-verifier" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "997c842047ffa2d011eb65bf638a3135b2d52bce5b20770fcc6040f1b48c624a" +checksum = "c4580df640d889c9f3c349cd2268968e44a99a8cf0df6c36ae5b1fb273712b00" dependencies = [ "bincode", - "miden-air", + "miden-air 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", "miden-core", "miden-crypto", "serde", @@ -1873,9 +1936,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", @@ -2061,9 +2124,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "p3-air" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ebc58ec27a174420348b3f04dba836fa2e5b5fe8df74601087417352757c643" +checksum = "9f2ec9cbfc642fc5173817287c3f8b789d07743b5f7e812d058b7a03e344f9ab" dependencies = [ "p3-field", "p3-matrix", @@ -2072,9 +2135,9 @@ dependencies = [ [[package]] name = "p3-blake3" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3cacb38c29fbee71fe3e5c6c0a1073632e46dc3e93fbdc50ab4e4fac137b525" +checksum = "b667f43b19499dd939c9e2553aa95688936a88360d50117dae3c8848d07dbc70" dependencies = [ "blake3", "p3-symmetric", @@ -2083,9 +2146,9 @@ dependencies = [ [[package]] name = "p3-challenger" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9bbcb18fe90271668259aacfc43455e328673c2b5c926cff0663edc8653e4d" +checksum = "4a0b490c745a7d2adeeafff06411814c8078c432740162332b3cd71be0158a76" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -2097,9 +2160,9 @@ dependencies = [ [[package]] name = "p3-commit" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d07b50c6f6d3bc89ed7c54ae0c569fb4caaa58263fd389dc02fb1b0a6378fa" +checksum = "916ae7989d5c3b49f887f5c55b2f9826bdbb81aaebf834503c4145d8b267c829" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2110,9 +2173,9 @@ dependencies = [ [[package]] name = "p3-dft" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17e7ba0dc20be075eab3f88f0cb820a0901f86218a1c46134e7c817d41597989" +checksum = "55301e91544440254977108b85c32c09d7ea05f2f0dd61092a2825339906a4a7" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2125,25 +2188,25 @@ dependencies = [ [[package]] name = "p3-field" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8533e6c2f4d0cc61fd2ae5299bb83316898e535f47291808d37e4d666ba088" +checksum = "85affca7fc983889f260655c4cf74163eebb94605f702e4b6809ead707cba54f" dependencies = [ "itertools 0.14.0", "num-bigint", "p3-maybe-rayon", "p3-util", "paste", - "rand 0.10.0", + "rand 0.10.1", "serde", "tracing", ] [[package]] name = "p3-goldilocks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8102a8c85acee1f896c3764bef5fac908e6026dadfc557c185294970cce0746" +checksum = "0ca1081f5c47b940f2d75a11c04f62ea1cc58a5d480dd465fef3861c045c63cd" dependencies = [ "num-bigint", "p3-challenger", @@ -2155,15 +2218,15 @@ dependencies = [ "p3-symmetric", "p3-util", "paste", - "rand 0.10.0", + "rand 0.10.1", "serde", ] [[package]] name = "p3-keccak" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65d30dd586d2855906a01c3414c155c2d564f6677d1b51f04186dcac080f757" +checksum = "ebcf27615ece1995e4fcf4c69740f1cf515d1481367a20b4b3ce7f4f1b8d70f7" dependencies = [ "p3-symmetric", "p3-util", @@ -2172,36 +2235,36 @@ dependencies = [ [[package]] name = "p3-matrix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72bb78444459155c2e4711d71abbfef7b04cc2ba1fa83751ccab241b01957095" +checksum = "53428126b009071563d1d07305a9de8be0d21de00b57d2475289ee32ffca6577" dependencies = [ "itertools 0.14.0", "p3-field", "p3-maybe-rayon", "p3-util", - "rand 0.10.0", + "rand 0.10.1", "serde", "tracing", ] [[package]] name = "p3-maybe-rayon" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a0a54345917f500130a9986fa5ff9ecbc26f0c6313080b35b713e26ddc8053" +checksum = "082bf467011c06c768c579ec6eb9accb5e1e62108891634cc770396e917f978a" [[package]] name = "p3-mds" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd514bf3e9bf9f1b7db2db96e5bd2972d9963dd62430de1e193d74522ae96a6" +checksum = "35209e6214102ea6ec6b8cb1b9c15a9b8e597a39f9173597c957f123bced81b3" dependencies = [ "p3-dft", "p3-field", "p3-symmetric", "p3-util", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] @@ -2232,7 +2295,7 @@ dependencies = [ "p3-miden-lmcs", "p3-miden-transcript", "p3-util", - "rand 0.10.0", + "rand 0.10.1", "thiserror 2.0.18", "tracing", ] @@ -2272,7 +2335,7 @@ dependencies = [ "p3-miden-transcript", "p3-symmetric", "p3-util", - "rand 0.10.0", + "rand 0.10.1", "serde", "thiserror 2.0.18", "tracing", @@ -2302,9 +2365,9 @@ dependencies = [ [[package]] name = "p3-monty-31" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d9340a650f07a6cd42a4e877017ba7b206df87fe50dfc3cf110f01a3c370bd1" +checksum = "ffa8c99ec50c035020bbf5457c6a729ba6a975719c1a8dd3f16421081e4f650c" dependencies = [ "itertools 0.14.0", "num-bigint", @@ -2318,7 +2381,7 @@ dependencies = [ "p3-symmetric", "p3-util", "paste", - "rand 0.10.0", + "rand 0.10.1", "serde", "spin 0.10.0", "tracing", @@ -2326,33 +2389,33 @@ dependencies = [ [[package]] name = "p3-poseidon1" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd56ae3a51ded1b77f7b1b21d0b157ae82b9d5ca8f2cba347c0b821fe771a79" +checksum = "6a018b618e3fa0aec8be933b1d8e404edd23f46991f6bf3f5c2f3f95e9413fe9" dependencies = [ "p3-field", "p3-symmetric", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] name = "p3-poseidon2" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "858aa1c33ec983dfbb8cfc553a213de19d8fde96485e54e6e952b9ac5e70bd4e" +checksum = "256a668a9ba916f8767552f13d0ba50d18968bc74a623bfdafa41e2970c944d0" dependencies = [ "p3-field", "p3-mds", "p3-symmetric", "p3-util", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] name = "p3-symmetric" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9a3b20bb8104e52d45219a78d80654c8ac6a4781be0eaa3f3e999f5ae4b9b2" +checksum = "6c60a71a1507c13611b0f2b0b6e83669fd5b76f8e3115bcbced5ccfdf3ca7807" dependencies = [ "itertools 0.14.0", "p3-field", @@ -2362,9 +2425,9 @@ dependencies = [ [[package]] name = "p3-util" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f24495d9cd64693165a9f1b3da0758395ad6d25d2d44dd740bdb34c2bce0c53" +checksum = "f8b766b9e9254bf3fa98d76e42cf8a5b30628c182dfd5272d270076ee12f0fc0" dependencies = [ "serde", "transpose", @@ -2436,9 +2499,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "poly1305" @@ -2491,6 +2554,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "priority-queue" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" +dependencies = [ + "equivalent", + "indexmap", + "serde", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -2510,7 +2584,7 @@ dependencies = [ "bit-vec", "bitflags", "num-traits", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -2519,6 +2593,20 @@ dependencies = [ "unarray", ] +[[package]] +name = "pubgrub" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f5df7e552bc7edd075f5783a87fbfc21d6a546e32c16985679c488c18192d83" +dependencies = [ + "indexmap", + "log", + "priority-queue", + "rustc-hash", + "thiserror 2.0.18", + "version-ranges", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2569,9 +2657,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -2579,13 +2667,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20 0.10.0", "getrandom 0.4.2", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -2628,9 +2716,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_hc" @@ -2673,9 +2761,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -2745,6 +2833,12 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2760,7 +2854,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -2881,9 +2975,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -2905,6 +2999,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -2960,9 +3066,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -3046,9 +3152,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "siphasher" @@ -3331,9 +3437,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "pin-project-lite", "tokio-macros", @@ -3341,9 +3447,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -3378,17 +3484,17 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.1.0", - "toml_datetime 1.1.0+spec-1.1.0", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] @@ -3402,9 +3508,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -3425,11 +3531,11 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] @@ -3440,9 +3546,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tracing" @@ -3528,7 +3634,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 1.1.0+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -3541,6 +3647,12 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.19.0" @@ -3622,6 +3734,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e9bd4e9c9ff6a2a9b5969462ba26216af3e010df0377dad8320ab515262ef8" +dependencies = [ + "smallvec", +] + [[package]] name = "version_check" version = "0.9.5" @@ -3682,9 +3803,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", @@ -3695,9 +3816,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3705,9 +3826,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ "bumpalo", "proc-macro2", @@ -3718,9 +3839,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] @@ -3756,7 +3877,7 @@ dependencies = [ "bitflags", "hashbrown 0.15.5", "indexmap", - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -3907,9 +4028,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" [[package]] name = "wit-bindgen" @@ -3991,7 +4112,7 @@ dependencies = [ "id-arena", "indexmap", "log", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -4020,18 +4141,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index 7a3d2e6..0feb04a 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -1,6 +1,9 @@ use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; -use miden_core::{Felt, operations::DebugVarInfo, operations::DebugVarLocation}; +use miden_core::{ + Felt, + operations::{DebugVarInfo, DebugVarLocation}, +}; use miden_processor::trace::RowIndex; /// A snapshot of a debug variable at a specific clock cycle. @@ -96,7 +99,10 @@ pub fn resolve_variable_value( DebugVarLocation::Memory(addr) => get_memory(*addr), DebugVarLocation::Const(felt) => Some(*felt), DebugVarLocation::Local(offset) => get_local(*offset), - DebugVarLocation::FrameBase { global_index, byte_offset } => { + DebugVarLocation::FrameBase { + global_index, + byte_offset, + } => { // global_index was resolved to a Miden byte address during compilation. // Convert to element address (÷4) to read the stack pointer value. let sp_elem_addr = *global_index / 4; diff --git a/crates/engine/src/exec/executor.rs b/crates/engine/src/exec/executor.rs index b2c7da7..0b112c0 100644 --- a/crates/engine/src/exec/executor.rs +++ b/crates/engine/src/exec/executor.rs @@ -8,8 +8,11 @@ use std::{ }; use miden_assembly_syntax::{Library, diagnostics::Report}; -use miden_core::Word; -use miden_core::program::{Program, StackInputs}; +use miden_core::{ + Word, + operations::DebugVarInfo, + program::{Program, StackInputs}, +}; use miden_debug_types::{SourceManager, SourceManagerExt}; use miden_mast_package::Dependency; use miden_processor::{ @@ -25,7 +28,6 @@ use crate::{ debug::{CallStack, DebugVarTracker}, felt::FromMidenRepr, }; -use miden_core::operations::DebugVarInfo; /// The [Executor] is responsible for executing a program with the Miden VM. /// diff --git a/src/config.rs b/src/config.rs index cd8c478..48ec89b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -34,7 +34,10 @@ pub struct DebuggerConfig { /// These arguments must be valid field element values expressed in decimal format. /// /// NOTE: These arguments will override any stack values provided via --inputs - #[cfg_attr(any(feature = "tui", feature = "repl"), arg(last(true), value_name = "ARGV"))] + #[cfg_attr( + any(feature = "tui", feature = "repl"), + arg(last(true), value_name = "ARGV") + )] pub args: Vec, /// The working directory for the debugger /// @@ -69,7 +72,10 @@ pub struct DebuggerConfig { pub color: ColorChoice, /// Specify the function to call as the entrypoint for the program /// in the format `::` - #[cfg_attr(any(feature = "tui", feature = "repl"), arg(long, help_heading = "Execution"))] + #[cfg_attr( + any(feature = "tui", feature = "repl"), + arg(long, help_heading = "Execution") + )] pub entrypoint: Option, /// Connect to a remote DAP debug server instead of running a local program. /// @@ -115,7 +121,10 @@ pub struct DebuggerConfig { )] pub link_libraries: Vec, /// Use the REPL (text-mode) debugger instead of the TUI - #[cfg_attr(any(feature = "tui", feature = "repl"), arg(long, help_heading = "Output"))] + #[cfg_attr( + any(feature = "tui", feature = "repl"), + arg(long, help_heading = "Output") + )] pub repl: bool, } diff --git a/src/lib.rs b/src/lib.rs index cb5205d..dd5747f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,11 +12,10 @@ pub(crate) mod ui; #[cfg(feature = "repl")] mod repl; -#[cfg(feature = "tui")] -pub use self::ui::{DebugMode, State, run, run_with_state}; - #[cfg(feature = "repl")] pub use self::repl::run as run_repl; +#[cfg(feature = "tui")] +pub use self::ui::{DebugMode, State, run, run_with_state}; pub use self::{ config::{ColorChoice, DebuggerConfig}, debug::*, diff --git a/src/main.rs b/src/main.rs index 2b08459..081e95d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,7 +59,9 @@ pub fn main() -> Result<(), Report> { return run(config, logger); #[cfg(not(feature = "tui"))] - Err(Report::msg("no UI feature enabled: build with --features tui or --features repl")) + Err(Report::msg( + "no UI feature enabled: build with --features tui or --features repl", + )) } fn setup_diagnostics() { diff --git a/src/ui/panes/source_code.rs b/src/ui/panes/source_code.rs index b8ee63d..9790271 100644 --- a/src/ui/panes/source_code.rs +++ b/src/ui/panes/source_code.rs @@ -131,16 +131,15 @@ struct Theme { current_line: Style, current_span: Style, line_number: Style, + #[allow(dead_code)] gutter_border: Style, } impl Default for Theme { fn default() -> Self { Self { focused_border_style: Style::default(), - current_line: Style::default() - .add_modifier(Modifier::BOLD | Modifier::REVERSED), - current_span: Style::default() - .add_modifier(Modifier::BOLD | Modifier::REVERSED), + current_line: Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED), + current_span: Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED), line_number: Style::default(), gutter_border: Style::default(), } @@ -379,7 +378,8 @@ impl Pane for SourceCodePane { let start = selected.start - line_span.start.to_usize(); start..start } else { - (selected.start - line_span.start.to_usize())..(selected.end - line_span.start.to_usize()) + (selected.start - line_span.start.to_usize()) + ..(selected.end - line_span.start.to_usize()) } }; let mut parts = syntect::util::modify_range( diff --git a/src/ui/panes/stacktrace.rs b/src/ui/panes/stacktrace.rs index 97b7f45..e3245d5 100644 --- a/src/ui/panes/stacktrace.rs +++ b/src/ui/panes/stacktrace.rs @@ -76,10 +76,7 @@ impl Pane for StackTracePane { let name = if is_top { Span::styled(name, Color::Gray) } else { - Span::styled( - name, - Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD), - ) + Span::styled(name, Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)) }; parts.push(name); if let Some(resolved) = frame.last_resolved(&state.source_manager) { diff --git a/src/ui/state.rs b/src/ui/state.rs index 44796ce..4374a8f 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -104,8 +104,9 @@ struct RemoteSnapshot { #[cfg(feature = "dap")] impl RemoteState { fn connect(addr: &str, source_manager: &Arc) -> Result { - use std::collections::BTreeSet; + use std::{cell::RefCell, collections::BTreeSet, rc::Rc}; + use miden_debug_engine::debug::DebugVarTracker; use miden_processor::{ContextId, FastProcessor}; use crate::exec::DebuggerHost; @@ -116,6 +117,7 @@ impl RemoteState { let processor = FastProcessor::new(StackInputs::default()); let host = DebuggerHost::new(source_manager.clone()); + let debug_vars = DebugVarTracker::new(Rc::new(RefCell::new(Default::default()))); let executor = DebugExecutor { processor, host, @@ -128,6 +130,7 @@ impl RemoteState { root_context: ContextId::root(), current_context: ContextId::root(), callstack: snapshot.callstack, + debug_vars, recent: VecDeque::new(), cycle: snapshot.cycle, stopped: false, @@ -685,9 +688,7 @@ impl State { // Use live processor state, not the pre-recorded trace, for current-cycle values. let read_mem = |addr: u32| -> Option { - executor.processor.memory() - .read_element(context, Felt::new(addr as u64)) - .ok() + executor.processor.memory().read_element(context, Felt::new(addr as u64)).ok() }; for var_snapshot in debug_vars.current_variables() { @@ -703,18 +704,13 @@ impl State { let location = var_snapshot.info.value_location(); - let value = resolve_variable_value( - location, - &stack, - |addr| read_mem(addr), - |offset| { - // Read FMP from live memory, then compute address as FMP + offset - let fmp_addr = miden_core::FMP_ADDR.as_canonical_u64() as u32; - let fmp = read_mem(fmp_addr)?; - let addr = (fmp.as_canonical_u64() as i64 + offset as i64) as u32; - read_mem(addr) - }, - ); + let value = resolve_variable_value(location, &stack, read_mem, |offset| { + // Read FMP from live memory, then compute address as FMP + offset + let fmp_addr = miden_core::FMP_ADDR.as_canonical_u64() as u32; + let fmp = read_mem(fmp_addr)?; + let addr = (fmp.as_canonical_u64() as i64 + offset as i64) as u32; + read_mem(addr) + }); match value { Some(felt) => { diff --git a/src/ui/syntax_highlighting.rs b/src/ui/syntax_highlighting.rs index 187c5d0..7fda284 100644 --- a/src/ui/syntax_highlighting.rs +++ b/src/ui/syntax_highlighting.rs @@ -259,7 +259,11 @@ pub fn convert_style(syntect_style: syntax::Style, use_bg_color: bool) -> Style let mut style = Style::new(); // Skip transparent colors (alpha=0) to use the terminal's native colors if fg.a > 0 { - let fg_color = if use_bg_color { blend_fg_color(syntect_style) } else { convert_color(fg) }; + let fg_color = if use_bg_color { + blend_fg_color(syntect_style) + } else { + convert_color(fg) + }; style = style.fg(fg_color); } if use_bg_color && bg.a > 0 { @@ -276,7 +280,12 @@ pub fn convert_to_syntect_style(style: Style, _use_bg_color: bool) -> syntax::St // Use transparent (alpha=0) fallbacks so that convert_style will skip // setting explicit colors, letting the terminal's native colors show through. // This avoids hardcoded White/Black that break on light/dark terminals. - let transparent = syntax::Color { r: 0, g: 0, b: 0, a: 0 }; + let transparent = syntax::Color { + r: 0, + g: 0, + b: 0, + a: 0, + }; syntax::Style { foreground: fg.unwrap_or(transparent), background: bg.unwrap_or(transparent), From 3172e84377621b1817cb494f229b840421f4b822 Mon Sep 17 00:00:00 2001 From: djole Date: Tue, 14 Apr 2026 16:49:51 +0200 Subject: [PATCH 09/12] fix(ui): procedure breakpoints match exec'd procedures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three UX/correctness fixes for the debugger session: 1. `b in ` now matches procedures called via `exec` (not just `call`). CallFrame.procedure is sticky — it's set when a new frame is pushed (by FrameStart trace events from `call`) and never updated afterwards, so for Rust programs that use `exec` internally the entire run appears to stay in the first seen procedure (e.g. `init`). Extract the live procedure from the current AsmOp's context_name instead of the frame, falling back to the frame's cached procedure when no asm op is present. 2. Continue past a terminated program now shows "program has terminated — use :r to restart" instead of silently doing nothing. 3. Add `:r` and `:restart` as aliases for the existing `:reload` command so the restart workflow is discoverable. Applied to both the TUI home page and the REPL session. --- src/repl/session.rs | 12 +++++++++++- src/ui/pages/home.rs | 25 +++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/repl/session.rs b/src/repl/session.rs index 713e483..dfad9ce 100644 --- a/src/repl/session.rs +++ b/src/repl/session.rs @@ -231,7 +231,16 @@ impl ReplSession { // Get current execution state for breakpoint checking let is_op_boundary = self.state.executor().current_asmop.is_some(); - let (proc, loc) = match self.state.executor().callstack.current_frame() { + // Live AsmOp context so exec'd procedures match `b in ` breakpoints + // (the callstack frame's procedure is sticky and only reflects the first + // procedure seen per CallFrame). + let live_proc: Option> = self + .state + .executor() + .current_asmop + .as_ref() + .map(|op| std::rc::Rc::from(op.context_name())); + let (frame_proc, loc) = match self.state.executor().callstack.current_frame() { Some(frame) => { let loc = frame .recent() @@ -242,6 +251,7 @@ impl ReplSession { } None => (None, None), }; + let proc = live_proc.or(frame_proc); // Check breakpoints let current_cycle = self.state.executor().cycle; diff --git a/src/ui/pages/home.rs b/src/ui/pages/home.rs index ffa25a0..aa7d7f5 100644 --- a/src/ui/pages/home.rs +++ b/src/ui/pages/home.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use miden_assembly_syntax::diagnostics::{IntoDiagnostic, Report}; use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, @@ -147,7 +149,7 @@ impl Page for Home { }, None => match args.trim() { "q" | "quit" => actions.push(Some(Action::Quit)), - "reload" => { + "r" | "reload" | "restart" => { actions.push(Some(Action::Reload)); } "debug" => { @@ -183,6 +185,16 @@ impl Page for Home { return Ok(None); } + // If the program has already terminated, there's nothing to run. + // Let the user know they can restart with `:r` / `:reload`. + if state.executor().stopped { + actions.push(Some(Action::TimedStatusLine( + "program has terminated — use :r to restart".into(), + 3, + ))); + return Ok(None); + } + let start_cycle = state.executor().cycle; let start_asmop = state.executor().current_asmop.clone(); let mut breakpoints = core::mem::take(&mut state.breakpoints); @@ -219,7 +231,15 @@ impl Page for Home { .as_ref() .map(|_info| true) .unwrap_or(false); - let (proc, loc) = match state.executor().callstack.current_frame() { + // Procedure matching uses the live AsmOp's context_name so that + // exec'd procedures (which share the caller's CallFrame) are still + // visible to `b in ` breakpoints. + let live_proc: Option> = state + .executor() + .current_asmop + .as_ref() + .map(|op| Rc::from(op.context_name())); + let (frame_proc, loc) = match state.executor().callstack.current_frame() { Some(frame) => { let loc = frame .recent() @@ -230,6 +250,7 @@ impl Page for Home { } None => (None, None), }; + let proc = live_proc.or(frame_proc); (op, is_boundary, proc, loc) }; From 46dce15be84895d5d7dacc1e112792c4b31e5e80 Mon Sep 17 00:00:00 2001 From: djole Date: Tue, 14 Apr 2026 16:52:53 +0200 Subject: [PATCH 10/12] feat(ui): add :proc / :p / :where command to show current procedure --- src/ui/pages/home.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ui/pages/home.rs b/src/ui/pages/home.rs index aa7d7f5..481fe28 100644 --- a/src/ui/pages/home.rs +++ b/src/ui/pages/home.rs @@ -159,6 +159,33 @@ impl Page for Home { let result = state.format_variables(false); actions.push(Some(Action::StatusLine(result))); } + "p" | "proc" | "where" => { + // Show the current procedure name so users can craft + // `b in ` breakpoints. + let live = state + .executor() + .current_asmop + .as_ref() + .map(|op| op.context_name().to_string()); + let frame = state + .executor() + .callstack + .current_frame() + .and_then(|f| f.procedure("")) + .map(|p| p.to_string()); + let msg = match (live, frame) { + (Some(l), Some(f)) if l == f => { + format!("proc: {l}") + } + (Some(l), Some(f)) => { + format!("proc (live): {l} / (frame): {f}") + } + (Some(l), None) => format!("proc (live): {l}"), + (None, Some(f)) => format!("proc (frame): {f}"), + (None, None) => "proc: ".to_string(), + }; + actions.push(Some(Action::StatusLine(msg))); + } invalid => { log::debug!("unknown command: '{invalid}'"); actions.push(Some(Action::TimedStatusLine("unknown command".into(), 1))) From 34736942a809ad5e3abfd15c4320f991aebd3341 Mon Sep 17 00:00:00 2001 From: djole Date: Tue, 14 Apr 2026 17:27:06 +0200 Subject: [PATCH 11/12] fix(ui): display live procedure in disassembly and stacktrace --- src/ui/panes/disasm.rs | 21 +++++++++++++++++---- src/ui/panes/stacktrace.rs | 13 +++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/ui/panes/disasm.rs b/src/ui/panes/disasm.rs index b51bf27..c493e99 100644 --- a/src/ui/panes/disasm.rs +++ b/src/ui/panes/disasm.rs @@ -57,9 +57,21 @@ impl Pane for DisassemblyPane { } fn draw(&mut self, frame: &mut Frame<'_>, area: Rect, state: &State) -> Result<(), Report> { + // Prefer the live AsmOp's context_name — the frame's procedure is sticky + // (set once on frame entry) so for programs that use `exec` instead of + // `call` the entire run appears to stay in the first seen procedure. + let live_proc_name = + state.executor().current_asmop.as_ref().map(|op| op.context_name().to_string()); + let (current_proc, lines) = match state.executor().callstack.current_frame() { None => { - let proc = Line::from("in ").right_aligned(); + let proc = Line::from( + live_proc_name + .as_deref() + .map(|p| format!("in {p}")) + .unwrap_or_else(|| "in ".to_string()), + ) + .right_aligned(); ( proc, state @@ -71,9 +83,10 @@ impl Pane for DisassemblyPane { ) } Some(frame) => { - let proc = frame - .procedure("") - .map(|proc| Line::from(format!("in {proc}"))) + let proc_name = + live_proc_name.clone().or_else(|| frame.procedure("").map(|p| p.to_string())); + let proc = proc_name + .map(|p| Line::from(format!("in {p}"))) .unwrap_or_else(|| Line::from("in ")) .right_aligned(); ( diff --git a/src/ui/panes/stacktrace.rs b/src/ui/panes/stacktrace.rs index e3245d5..1aa2e2a 100644 --- a/src/ui/panes/stacktrace.rs +++ b/src/ui/panes/stacktrace.rs @@ -59,6 +59,11 @@ impl Pane for StackTracePane { fn draw(&mut self, frame: &mut Frame<'_>, area: Rect, state: &State) -> Result<(), Report> { let mut lines = Vec::default(); let num_frames = state.executor().callstack.frames().len(); + // For the top frame, prefer the live AsmOp's context_name over the + // frame's cached procedure, which is set once on frame entry and + // stays stale for programs that use `exec` instead of `call`. + let live_top_name: Option = + state.executor().current_asmop.as_ref().map(|op| op.context_name().to_string()); for (i, frame) in state.executor().callstack.frames().iter().enumerate() { let is_top = i + 1 == num_frames; let mut parts = vec![]; @@ -71,8 +76,12 @@ impl Pane for StackTracePane { */ let gutter = Span::styled(" ", Color::Gray); parts.push(gutter); - let name = frame.procedure(""); - let name = name.as_deref().unwrap_or("").to_string(); + let name = if is_top { + live_top_name.clone().or_else(|| frame.procedure("").map(|p| p.to_string())) + } else { + frame.procedure("").map(|p| p.to_string()) + }; + let name = name.unwrap_or_else(|| "".to_string()); let name = if is_top { Span::styled(name, Color::Gray) } else { From 279bb6ec4ac664167e9378433ffbac84e132f34a Mon Sep 17 00:00:00 2001 From: djole Date: Tue, 14 Apr 2026 22:04:03 +0200 Subject: [PATCH 12/12] fix: resolve source variables from frame base --- crates/engine/src/debug/breakpoint.rs | 11 +- crates/engine/src/debug/variables.rs | 18 ++++ crates/engine/src/exec/executor.rs | 4 + crates/engine/src/exec/state.rs | 47 ++++++++- src/repl/commands.rs | 12 ++- src/repl/session.rs | 144 ++++++++++++++++++-------- src/ui/pages/home.rs | 114 ++++++++++++++------ src/ui/panes/breakpoints.rs | 7 +- src/ui/panes/source_code.rs | 11 +- src/ui/state.rs | 78 +++++++++++++- src/ui/syntax_highlighting.rs | 16 +++ 11 files changed, 373 insertions(+), 89 deletions(-) diff --git a/crates/engine/src/debug/breakpoint.rs b/crates/engine/src/debug/breakpoint.rs index bd5fd9d..0ca5933 100644 --- a/crates/engine/src/debug/breakpoint.rs +++ b/crates/engine/src/debug/breakpoint.rs @@ -54,6 +54,8 @@ pub enum BreakpointType { StepTo(usize), /// Break at the first cycle of the next instruction Next, + /// Break at the next source line, or the next instruction if no source location is available. + NextLine, /// Break when we exit the current call frame Finish, /// Break when any cycle corresponds to a source location whose file matches PATTERN @@ -100,7 +102,13 @@ impl BreakpointType { /// Returns true if this breakpoint is internal to the debugger (i.e. not creatable via :b) pub fn is_internal(&self) -> bool { - matches!(self, BreakpointType::Next | BreakpointType::Step | BreakpointType::Finish) + matches!( + self, + BreakpointType::Next + | BreakpointType::NextLine + | BreakpointType::Step + | BreakpointType::Finish + ) } /// Returns true if this breakpoint is removed upon being hit @@ -108,6 +116,7 @@ impl BreakpointType { matches!( self, BreakpointType::Next + | BreakpointType::NextLine | BreakpointType::Finish | BreakpointType::Step | BreakpointType::StepN(_) diff --git a/crates/engine/src/debug/variables.rs b/crates/engine/src/debug/variables.rs index 0feb04a..7be36f8 100644 --- a/crates/engine/src/debug/variables.rs +++ b/crates/engine/src/debug/variables.rs @@ -6,6 +6,17 @@ use miden_core::{ }; use miden_processor::trace::RowIndex; +const FRAME_BASE_LOCAL_MARKER: u32 = 1 << 31; + +fn decode_frame_base_local_offset(encoded: u32) -> Option { + if encoded & FRAME_BASE_LOCAL_MARKER == 0 { + return None; + } + + let low_bits = (encoded & 0xffff) as u16; + Some(i16::from_le_bytes(low_bits.to_le_bytes())) +} + /// A snapshot of a debug variable at a specific clock cycle. #[derive(Debug, Clone)] pub struct DebugVarSnapshot { @@ -103,6 +114,13 @@ pub fn resolve_variable_value( global_index, byte_offset, } => { + if let Some(local_offset) = decode_frame_base_local_offset(*global_index) { + let base = get_local(local_offset)?; + let byte_addr = base.as_canonical_u64() as i64 + byte_offset; + let elem_addr = u32::try_from(byte_addr / 4).ok()?; + return get_memory(elem_addr); + } + // global_index was resolved to a Miden byte address during compilation. // Convert to element address (÷4) to read the stack pointer value. let sp_elem_addr = *global_index / 4; diff --git a/crates/engine/src/exec/executor.rs b/crates/engine/src/exec/executor.rs index 0b112c0..d05b5fe 100644 --- a/crates/engine/src/exec/executor.rs +++ b/crates/engine/src/exec/executor.rs @@ -200,7 +200,9 @@ impl Executor { root_context, current_context: root_context, callstack, + current_proc: None, debug_vars, + last_debug_var_count: 0, recent: VecDeque::with_capacity(5), cycle: 0, stopped: false, @@ -275,7 +277,9 @@ impl Executor { root_context, current_context: root_context, callstack, + current_proc: None, debug_vars, + last_debug_var_count: 0, recent: VecDeque::with_capacity(5), cycle: 0, stopped: false, diff --git a/crates/engine/src/exec/state.rs b/crates/engine/src/exec/state.rs index e5bf057..d936188 100644 --- a/crates/engine/src/exec/state.rs +++ b/crates/engine/src/exec/state.rs @@ -1,4 +1,7 @@ -use std::collections::{BTreeSet, VecDeque}; +use std::{ + collections::{BTreeSet, VecDeque}, + rc::Rc, +}; use miden_core::{ mast::{MastNode, MastNodeId}, @@ -60,8 +63,12 @@ pub struct DebugExecutor { pub current_context: ContextId, /// The current call stack pub callstack: CallStack, + /// The most recent live procedure name observed from assembly operation metadata. + pub current_proc: Option>, /// Debug variable tracker for source-level variable inspection pub debug_vars: DebugVarTracker, + /// Number of debug variable location records observed during the most recent step. + pub last_debug_var_count: usize, /// A sliding window of the last 5 operations successfully executed by the VM pub recent: VecDeque, /// The current clock cycle @@ -136,6 +143,35 @@ pub(crate) fn extract_current_op( } impl DebugExecutor { + /// Returns true if the current program forest has debug-variable locations associated with + /// `procedure`. + pub fn procedure_has_debug_vars(&self, procedure: &str) -> bool { + let Some(resume_ctx) = self.resume_ctx.as_ref() else { + return false; + }; + + let forest = resume_ctx.current_forest(); + for (node_idx, node) in forest.nodes().iter().enumerate() { + let MastNode::Block(block) = node else { + continue; + }; + let node_id = MastNodeId::new_unchecked(node_idx as u32); + for op_idx in 0..block.num_operations() as usize { + if forest.debug_vars_for_operation(node_id, op_idx).is_empty() { + continue; + } + if forest + .get_assembly_op(node_id, Some(op_idx)) + .is_some_and(|op| op.context_name() == procedure) + { + return true; + } + } + } + + false + } + /// Advance the program state by one cycle. /// /// If the program has already reached its termination state, it returns the same result @@ -144,6 +180,7 @@ impl DebugExecutor { /// Returns the call frame exited this cycle, if any pub fn step(&mut self) -> Result, ExecutionError> { if self.stopped { + self.last_debug_var_count = 0; return Ok(None); } @@ -151,6 +188,7 @@ impl DebugExecutor { Some(ctx) => ctx, None => { self.stopped = true; + self.last_debug_var_count = 0; return Ok(None); } }; @@ -191,6 +229,9 @@ impl DebugExecutor { // Track operation self.current_op = op; self.current_asmop = asmop.clone(); + if let Some(asmop) = asmop.as_ref() { + self.current_proc = Some(Rc::from(asmop.context_name())); + } if let Some(op) = op { if self.recent.len() == 5 { @@ -210,15 +251,18 @@ impl DebugExecutor { let exited = self.callstack.next(&step_info); // Record and process debug variable events + let debug_var_count = debug_var_infos.len(); self.debug_vars .record_events(RowIndex::from(self.cycle as u32), debug_var_infos); self.debug_vars.update_to_cycle(RowIndex::from(self.cycle as u32)); + self.last_debug_var_count = debug_var_count; Ok(exited) } Ok(None) => { // Program completed self.stopped = true; + self.last_debug_var_count = 0; let state = self.processor.state(); self.current_stack = state.get_stack_state(); @@ -230,6 +274,7 @@ impl DebugExecutor { } Err(err) => { self.stopped = true; + self.last_debug_var_count = 0; Err(err) } } diff --git a/src/repl/commands.rs b/src/repl/commands.rs index c05f6a2..2669492 100644 --- a/src/repl/commands.rs +++ b/src/repl/commands.rs @@ -11,6 +11,8 @@ pub enum ReplCommand { StepN(usize), /// Execute until next instruction boundary Next, + /// Execute until next source line + NextLine, /// Run until breakpoint or end Continue, /// Run until current function returns @@ -27,8 +29,8 @@ pub enum ReplCommand { Memory(ReadMemoryExpr), /// Show local variables Locals, - /// Show all debug variables - Vars, + /// Show debug variables, including compiler-generated locals if true. + Vars(bool), /// Show current source location Where, /// Show recent instructions @@ -68,6 +70,7 @@ impl FromStr for ReplCommand { None => Ok(ReplCommand::Step), }, "n" | "next" => Ok(ReplCommand::Next), + "nl" | "next-line" | "nextline" => Ok(ReplCommand::NextLine), "c" | "continue" => Ok(ReplCommand::Continue), "e" | "finish" => Ok(ReplCommand::Finish), @@ -94,7 +97,7 @@ impl FromStr for ReplCommand { Ok(ReplCommand::Memory(expr)) } "locals" => Ok(ReplCommand::Locals), - "vars" | "variables" => Ok(ReplCommand::Vars), + "vars" | "variables" => Ok(ReplCommand::Vars(args == Some("all"))), "where" | "w" => Ok(ReplCommand::Where), "l" | "list" => Ok(ReplCommand::List), "bt" | "backtrace" => Ok(ReplCommand::Backtrace), @@ -117,6 +120,7 @@ impl ReplCommand { Execution: s, step [N] Execute one (or N) VM cycle(s) n, next Execute until next instruction boundary + nl, next-line Execute until next source line c, continue Run until breakpoint or end e, finish Run until current function returns reload Restart program execution @@ -131,7 +135,7 @@ Inspection: stack Show operand stack mem [type] Show memory at address (e.g., mem 0x100 u32) locals Show local variables - vars Show all debug variables + vars [all] Show source variables; include compiler locals with `all` where Show current source location l, list Show recent instructions bt, backtrace Show call stack diff --git a/src/repl/session.rs b/src/repl/session.rs index dfad9ce..198f801 100644 --- a/src/repl/session.rs +++ b/src/repl/session.rs @@ -100,13 +100,11 @@ impl ReplSession { } fn print_location(&self) { - if let Some(frame) = self.state.executor().callstack.current_frame() { - if let Some(resolved) = frame.last_resolved(&*self.state.source_manager) { - let proc_name = frame.procedure("").unwrap_or_else(|| Rc::from("")); - println!("at {} in {}", resolved, proc_name); - } else if let Some(proc_name) = frame.procedure("") { - println!("in {}", proc_name); - } + let proc_name = self.state.current_procedure().unwrap_or_else(|| Rc::from("")); + if let Some(resolved) = self.state.current_display_location() { + println!("at {} in {}", resolved, proc_name); + } else if self.state.executor().callstack.current_frame().is_some() { + println!("in {}", proc_name); } } @@ -115,6 +113,7 @@ impl ReplSession { ReplCommand::Step => self.cmd_step(1), ReplCommand::StepN(n) => self.cmd_step(n), ReplCommand::Next => self.cmd_next(), + ReplCommand::NextLine => self.cmd_next_line(), ReplCommand::Continue => self.cmd_continue(), ReplCommand::Finish => self.cmd_finish(), ReplCommand::Break(bp_type) => self.cmd_break(bp_type), @@ -123,7 +122,7 @@ impl ReplSession { ReplCommand::Stack => self.cmd_stack(), ReplCommand::Memory(expr) => self.cmd_memory(&expr), ReplCommand::Locals => self.cmd_locals(), - ReplCommand::Vars => self.cmd_vars(), + ReplCommand::Vars(show_all) => self.cmd_vars(show_all), ReplCommand::Where => self.cmd_where(), ReplCommand::List => self.cmd_list(), ReplCommand::Backtrace => self.cmd_backtrace(), @@ -168,6 +167,18 @@ impl ReplSession { Ok(()) } + fn cmd_next_line(&mut self) -> Result<(), String> { + if self.state.executor().stopped { + return Err("program has terminated, cannot continue".into()); + } + + self.state.create_breakpoint(BreakpointType::NextLine); + self.state.stopped = false; + self.run_until_stopped(); + self.print_location(); + Ok(()) + } + fn cmd_continue(&mut self) -> Result<(), String> { if self.state.executor().stopped { return Err("program has terminated, cannot continue".into()); @@ -203,7 +214,13 @@ impl ReplSession { fn run_until_stopped(&mut self) { let start_cycle = self.state.executor().cycle; + let start_asmop = self.state.executor().current_asmop.clone(); + let start_proc = self.state.current_procedure(); + let start_line_loc = self.state.current_display_location(); + let mut previous_proc = self.state.current_procedure(); + let mut pending_called_breakpoints = Vec::new(); let mut breakpoints: Vec = core::mem::take(&mut self.state.breakpoints); + self.state.breakpoints_hit.clear(); loop { // Check if program has terminated @@ -231,27 +248,9 @@ impl ReplSession { // Get current execution state for breakpoint checking let is_op_boundary = self.state.executor().current_asmop.is_some(); - // Live AsmOp context so exec'd procedures match `b in ` breakpoints - // (the callstack frame's procedure is sticky and only reflects the first - // procedure seen per CallFrame). - let live_proc: Option> = self - .state - .executor() - .current_asmop - .as_ref() - .map(|op| std::rc::Rc::from(op.context_name())); - let (frame_proc, loc) = match self.state.executor().callstack.current_frame() { - Some(frame) => { - let loc = frame - .recent() - .back() - .and_then(|detail| detail.resolve(&*self.state.source_manager)) - .cloned(); - (frame.procedure(""), loc) - } - None => (None, None), - }; - let proc = live_proc.or(frame_proc); + let loc = self.state.current_location(); + let line_loc = self.state.current_display_location(); + let proc = self.state.current_procedure(); // Check breakpoints let current_cycle = self.state.executor().cycle; @@ -271,25 +270,38 @@ impl ReplSession { return true; } - if cycles_stepped > 0 && is_op_boundary && matches!(&bp.ty, BreakpointType::Next) { + if cycles_stepped > 0 + && is_op_boundary + && matches!(&bp.ty, BreakpointType::Next) + && self.state.executor().current_asmop != start_asmop + { self.state.breakpoints_hit.push(core::mem::take(bp)); return false; } - if let Some(loc) = loc.as_ref() - && bp.should_break_at(loc) + if cycles_stepped > 0 + && is_op_boundary + && matches!(&bp.ty, BreakpointType::NextLine) { - let retained = !bp.is_one_shot(); - if retained { - self.state.breakpoints_hit.push(bp.clone()); + let has_source_context = start_line_loc.is_some() || line_loc.is_some(); + let reached_next = if has_source_context { + State::is_next_source_line( + start_proc.as_deref(), + start_line_loc.as_ref(), + proc.as_deref(), + line_loc.as_ref(), + ) } else { + self.state.executor().current_asmop != start_asmop + }; + if reached_next { self.state.breakpoints_hit.push(core::mem::take(bp)); + return false; } - return retained; } - if let Some(proc) = proc.as_deref() - && bp.should_break_in(proc) + if let Some(loc) = loc.as_ref() + && bp.should_break_at(loc) { let retained = !bp.is_one_shot(); if retained { @@ -300,6 +312,47 @@ impl ReplSession { return retained; } + if matches!(&bp.ty, BreakpointType::Called(_)) + && let Some(proc) = proc.as_deref() + { + let matched = bp.should_break_in(proc); + if !matched { + pending_called_breakpoints.retain(|id| *id != bp.id); + return true; + } + + let was_matched = previous_proc + .as_deref() + .is_some_and(|previous| bp.should_break_in(previous)); + let matched_at_start = + start_proc.as_deref().is_some_and(|start| bp.should_break_in(start)); + let pending = pending_called_breakpoints.contains(&bp.id); + let entered_matching_proc = !was_matched && !matched_at_start; + + if entered_matching_proc + && self.state.executor().procedure_has_debug_vars(proc) + && self.state.executor().last_debug_var_count == 0 + { + if !pending { + pending_called_breakpoints.push(bp.id); + } + return true; + } + + if entered_matching_proc + || (pending && self.state.executor().last_debug_var_count > 0) + { + pending_called_breakpoints.retain(|id| *id != bp.id); + let retained = !bp.is_one_shot(); + if retained { + self.state.breakpoints_hit.push(bp.clone()); + } else { + self.state.breakpoints_hit.push(core::mem::take(bp)); + } + return retained; + } + } + true }); @@ -322,6 +375,8 @@ impl ReplSession { self.state.stopped = true; break; } + + previous_proc = proc; } // Restore breakpoints @@ -399,17 +454,17 @@ impl ReplSession { Ok(()) } - fn cmd_vars(&mut self) -> Result<(), String> { - let output = self.state.format_variables(false); + fn cmd_vars(&mut self, show_all: bool) -> Result<(), String> { + let output = self.state.format_variables(show_all); println!("{}", output); Ok(()) } fn cmd_where(&mut self) -> Result<(), String> { - if let Some(frame) = self.state.executor().callstack.current_frame() { - let proc_name = frame.procedure("").unwrap_or_else(|| Rc::from("")); + if self.state.executor().callstack.current_frame().is_some() { + let proc_name = self.state.current_procedure().unwrap_or_else(|| Rc::from("")); - if let Some(resolved) = frame.last_resolved(&*self.state.source_manager) { + if let Some(resolved) = self.state.current_display_location() { println!( "{}:{}:{} in {}", resolved.source_file.uri().as_str(), @@ -484,8 +539,9 @@ fn format_bp_type(ty: &BreakpointType) -> String { BreakpointType::StepN(n) => format!("after {} cycles", n), BreakpointType::StepTo(c) => format!("at cycle {}", c), BreakpointType::Next => "next instruction".into(), + BreakpointType::NextLine => "next source line".into(), BreakpointType::Finish => "function return".into(), - BreakpointType::File(pat) => format!("{}", pat.as_str()), + BreakpointType::File(pat) => pat.as_str().to_string(), BreakpointType::Line { pattern, line } => format!("{}:{}", pattern.as_str(), line), BreakpointType::Opcode(op) => format!("opcode {:?}", op), BreakpointType::Called(pat) => format!("call {}", pat.as_str()), diff --git a/src/ui/pages/home.rs b/src/ui/pages/home.rs index 481fe28..a1775ca 100644 --- a/src/ui/pages/home.rs +++ b/src/ui/pages/home.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use miden_assembly_syntax::diagnostics::{IntoDiagnostic, Report}; use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, @@ -152,6 +150,18 @@ impl Page for Home { "r" | "reload" | "restart" => { actions.push(Some(Action::Reload)); } + "nl" | "next-line" | "nextline" => { + if state.stopped && !state.executor().stopped { + state.create_breakpoint(BreakpointType::NextLine); + state.stopped = false; + actions.push(Some(Action::Continue)); + } else if state.executor().stopped { + actions.push(Some(Action::TimedStatusLine( + "program has terminated, cannot continue".to_string(), + 3, + ))); + } + } "debug" => { actions.push(Some(Action::ShowDebug)); } @@ -224,7 +234,12 @@ impl Page for Home { let start_cycle = state.executor().cycle; let start_asmop = state.executor().current_asmop.clone(); + let start_proc = state.current_procedure(); + let start_line_loc = state.current_display_location(); + let mut previous_proc = state.current_procedure(); + let mut pending_called_breakpoints = Vec::new(); let mut breakpoints = core::mem::take(&mut state.breakpoints); + state.breakpoints_hit.clear(); state.stopped = false; let stopped = loop { // If stepping the program results in the program terminating succesfully, stop @@ -250,7 +265,7 @@ impl Page for Home { continue; } - let (_op, is_op_boundary, proc, loc) = { + let (_op, is_op_boundary, proc, loc, line_loc) = { let op = state.executor().current_op; let is_boundary = state .executor() @@ -258,27 +273,10 @@ impl Page for Home { .as_ref() .map(|_info| true) .unwrap_or(false); - // Procedure matching uses the live AsmOp's context_name so that - // exec'd procedures (which share the caller's CallFrame) are still - // visible to `b in ` breakpoints. - let live_proc: Option> = state - .executor() - .current_asmop - .as_ref() - .map(|op| Rc::from(op.context_name())); - let (frame_proc, loc) = match state.executor().callstack.current_frame() { - Some(frame) => { - let loc = frame - .recent() - .back() - .and_then(|detail| detail.resolve(&state.source_manager)) - .cloned(); - (frame.procedure(""), loc) - } - None => (None, None), - }; - let proc = live_proc.or(frame_proc); - (op, is_boundary, proc, loc) + let loc = state.current_location(); + let line_loc = state.current_display_location(); + let proc = state.current_procedure(); + (op, is_boundary, proc, loc, line_loc) }; // Remove all breakpoints triggered at this cycle @@ -308,20 +306,29 @@ impl Page for Home { return false; } - if let Some(loc) = loc.as_ref() - && bp.should_break_at(loc) + if cycles_stepped > 0 + && is_op_boundary + && matches!(&bp.ty, BreakpointType::NextLine) { - let retained = !bp.is_one_shot(); - if retained { - state.breakpoints_hit.push(bp.clone()); + let has_source_context = start_line_loc.is_some() || line_loc.is_some(); + let reached_next = if has_source_context { + State::is_next_source_line( + start_proc.as_deref(), + start_line_loc.as_ref(), + proc.as_deref(), + line_loc.as_ref(), + ) } else { + state.executor().current_asmop != start_asmop + }; + if reached_next { state.breakpoints_hit.push(core::mem::take(bp)); + return false; } - return retained; } - if let Some(proc) = proc.as_deref() - && bp.should_break_in(proc) + if let Some(loc) = loc.as_ref() + && bp.should_break_at(loc) { let retained = !bp.is_one_shot(); if retained { @@ -332,6 +339,47 @@ impl Page for Home { return retained; } + if matches!(&bp.ty, BreakpointType::Called(_)) + && let Some(proc) = proc.as_deref() + { + let matched = bp.should_break_in(proc); + if !matched { + pending_called_breakpoints.retain(|id| *id != bp.id); + return true; + } + + let was_matched = previous_proc + .as_deref() + .is_some_and(|previous| bp.should_break_in(previous)); + let matched_at_start = start_proc + .as_deref() + .is_some_and(|start| bp.should_break_in(start)); + let pending = pending_called_breakpoints.contains(&bp.id); + let entered_matching_proc = !was_matched && !matched_at_start; + if entered_matching_proc + && state.executor().procedure_has_debug_vars(proc) + && state.executor().last_debug_var_count == 0 + { + if !pending { + pending_called_breakpoints.push(bp.id); + } + return true; + } + + if entered_matching_proc + || (pending && state.executor().last_debug_var_count > 0) + { + pending_called_breakpoints.retain(|id| *id != bp.id); + let retained = !bp.is_one_shot(); + if retained { + state.breakpoints_hit.push(bp.clone()); + } else { + state.breakpoints_hit.push(core::mem::take(bp)); + } + return retained; + } + } + true }); @@ -351,6 +399,8 @@ impl Page for Home { if !state.breakpoints_hit.is_empty() { break true; } + + previous_proc = proc; }; // Restore the breakpoints state diff --git a/src/ui/panes/breakpoints.rs b/src/ui/panes/breakpoints.rs index cd855b8..c61c245 100644 --- a/src/ui/panes/breakpoints.rs +++ b/src/ui/panes/breakpoints.rs @@ -171,9 +171,10 @@ impl Pane for BreakpointsPane { Span::styled("", Style::default()) }; let line = match &bp.ty { - BreakpointType::Next | BreakpointType::Step | BreakpointType::Finish => { - unreachable!() - } + BreakpointType::Next + | BreakpointType::NextLine + | BreakpointType::Step + | BreakpointType::Finish => unreachable!(), BreakpointType::StepN(n) => Line::from(vec![ gutter, Span::styled("cycle:", yellow), diff --git a/src/ui/panes/source_code.rs b/src/ui/panes/source_code.rs index 9790271..0012867 100644 --- a/src/ui/panes/source_code.rs +++ b/src/ui/panes/source_code.rs @@ -15,7 +15,10 @@ use crate::{ action::Action, panes::Pane, state::State, - syntax_highlighting::{Highlighter, HighlighterState, NoopHighlighter, SyntectHighlighter}, + syntax_highlighting::{ + Highlighter, HighlighterState, NoopHighlighter, SyntectHighlighter, + clamp_byte_selection_to_str, + }, tui::Frame, }, }; @@ -83,6 +86,7 @@ impl SourceCodePane { } else { (resolved_span.start - span.start)..(resolved_span.end - span.start) }; + let selection = clamp_byte_selection_to_str(&line_content, selection); highlighter_state.highlight_line_with_selection( line_content.into(), selection, @@ -382,6 +386,11 @@ impl Pane for SourceCodePane { ..(selected.end - line_span.start.to_usize()) } }; + let selected_line_content = selected_line_deconstructed + .iter() + .map(|(_, content)| *content) + .collect::(); + let selected = clamp_byte_selection_to_str(&selected_line_content, selected); let mut parts = syntect::util::modify_range( selected_line_deconstructed.as_slice(), selected, diff --git a/src/ui/state.rs b/src/ui/state.rs index 4374a8f..8fe9f81 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -1,4 +1,4 @@ -use std::{collections::VecDeque, sync::Arc}; +use std::{collections::VecDeque, rc::Rc, sync::Arc}; use miden_assembly::{DefaultSourceManager, SourceManager}; use miden_assembly_syntax::diagnostics::{IntoDiagnostic, Report}; @@ -11,7 +11,7 @@ use miden_processor::{ use crate::{ config::DebuggerConfig, - debug::{Breakpoint, BreakpointType, ReadMemoryExpr, resolve_variable_value}, + debug::{Breakpoint, BreakpointType, ReadMemoryExpr, ResolvedLocation, resolve_variable_value}, exec::{DebugExecutor, ExecutionTrace, Executor}, input::InputFile, }; @@ -130,7 +130,9 @@ impl RemoteState { root_context: ContextId::root(), current_context: ContextId::root(), callstack: snapshot.callstack, + current_proc: None, debug_vars, + last_debug_var_count: 0, recent: VecDeque::new(), cycle: snapshot.cycle, stopped: false, @@ -197,7 +199,9 @@ impl RemoteState { self.sync_breakpoints(breakpoints); let has_step = breakpoints.iter().any(|bp| matches!(bp.ty, BreakpointType::Step)); - let has_next = breakpoints.iter().any(|bp| matches!(bp.ty, BreakpointType::Next)); + let has_next = breakpoints + .iter() + .any(|bp| matches!(bp.ty, BreakpointType::Next | BreakpointType::NextLine)); let has_finish = breakpoints.iter().any(|bp| matches!(bp.ty, BreakpointType::Finish)); if has_step { @@ -525,6 +529,60 @@ impl State { } } + pub fn current_procedure(&self) -> Option> { + let live_proc = self + .executor() + .current_asmop + .as_ref() + .map(|op| Rc::from(op.context_name())) + .or_else(|| self.executor().current_proc.clone()); + let frame_proc = + self.executor().callstack.current_frame().and_then(|frame| frame.procedure("")); + live_proc.or(frame_proc) + } + + pub fn current_location(&self) -> Option { + self.executor() + .callstack + .current_frame() + .and_then(|frame| frame.recent().back()) + .and_then(|detail| detail.resolve(&*self.source_manager)) + .cloned() + } + + pub fn current_display_location(&self) -> Option { + self.executor() + .callstack + .current_frame() + .and_then(|frame| frame.last_resolved(&*self.source_manager)) + .cloned() + } + + pub fn is_next_source_line( + start_proc: Option<&str>, + start_loc: Option<&ResolvedLocation>, + current_proc: Option<&str>, + current_loc: Option<&ResolvedLocation>, + ) -> bool { + let same_proc = match (start_proc, current_proc) { + (Some(start), Some(current)) => start == current, + (Some(_), None) => false, + _ => true, + }; + if !same_proc { + return false; + } + + match (start_loc, current_loc) { + (Some(start), Some(current)) => { + start.source_file.uri().as_str() == current.source_file.uri().as_str() + && start.line != current.line + } + (None, Some(_)) => true, + _ => false, + } + } + pub fn execution_failed(&self) -> Option<&miden_processor::ExecutionError> { match &self.session { SessionState::Local(local) => local.execution_failed.as_ref(), @@ -691,6 +749,12 @@ impl State { executor.processor.memory().read_element(context, Felt::new(addr as u64)).ok() }; + let current_source = if show_all { + None + } else { + self.current_display_location() + }; + for var_snapshot in debug_vars.current_variables() { let name = var_snapshot.info.name(); @@ -698,6 +762,14 @@ impl State { continue; } + if let (Some(current), Some(var_loc)) = + (current_source.as_ref(), var_snapshot.info.location()) + && (var_loc.uri.as_str() != current.source_file.uri().as_str() + || var_loc.line.to_u32() > current.line) + { + continue; + } + if !output.is_empty() { output.push_str(", "); } diff --git a/src/ui/syntax_highlighting.rs b/src/ui/syntax_highlighting.rs index 7fda284..83491e0 100644 --- a/src/ui/syntax_highlighting.rs +++ b/src/ui/syntax_highlighting.rs @@ -95,6 +95,7 @@ fn default_line_with_selection( selected: Range, style: Style, ) -> Vec> { + let selected = clamp_byte_selection_to_str(&line, selected); let prefix_content = core::str::from_utf8(&line.as_bytes()[..selected.start]).expect("invalid selection"); let selected_content = @@ -113,6 +114,20 @@ fn default_line_with_selection( ] } +pub fn clamp_byte_selection_to_str(line: &str, selected: Range) -> Range { + fn floor_char_boundary(line: &str, idx: usize) -> usize { + let mut idx = idx.min(line.len()); + while idx > 0 && !line.is_char_boundary(idx) { + idx -= 1; + } + idx + } + + let start = floor_char_boundary(line, selected.start); + let end = floor_char_boundary(line, selected.end).max(start); + start..end +} + /// Syntax highlighting provided by [syntect](https://docs.rs/syntect/latest/syntect/). /// /// Currently only 24-bit truecolor output is supported due to syntect themes @@ -221,6 +236,7 @@ impl HighlighterState for SyntectHighlighterState<'_> { selected: Range, style: Style, ) -> Vec> { + let selected = clamp_byte_selection_to_str(&line, selected); if let Ok(ops) = self.parse_state.parse_line(&line, &self.syntax_set) { let use_bg_color = self.use_bg_color; let parts = syntax::HighlightIterator::new(