From 7ae0b1232b9b89c3b2dd63c340166fb561e7abe5 Mon Sep 17 00:00:00 2001 From: piotmag769 Date: Mon, 16 Feb 2026 19:15:35 +0100 Subject: [PATCH 1/3] Fix depth length and object references --- src/debugger.rs | 11 +++- src/debugger/call_stack.rs | 117 +++++++++++++++++++++++-------------- src/debugger/handler.rs | 8 ++- 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/src/debugger.rs b/src/debugger.rs index 3021b32..6a4172e 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -105,11 +105,18 @@ impl CairoDebugger { let stop = match &self.state.step_action { Some(StepAction::StepIn { prev_line }) if *prev_line != current_line => true, Some(StepAction::Next { prev_line, depth }) - if *depth >= self.state.call_stack.depth() && *prev_line != current_line => + if *depth + >= self.state.call_stack.depth(self.state.current_statement_idx, &self.ctx) + && *prev_line != current_line => + { + true + } + Some(StepAction::StepOut { depth }) + if *depth + > self.state.call_stack.depth(self.state.current_statement_idx, &self.ctx) => { true } - Some(StepAction::StepOut { depth }) if *depth > self.state.call_stack.depth() => true, _ => false, }; diff --git a/src/debugger/call_stack.rs b/src/debugger/call_stack.rs index 19664ab..264c6b6 100644 --- a/src/debugger/call_stack.rs +++ b/src/debugger/call_stack.rs @@ -1,4 +1,4 @@ -use std::iter::once; +use std::iter; use std::path::Path; use cairo_annotations::annotations::coverage::{CodeLocation, SourceFileFullPath}; @@ -12,13 +12,19 @@ use crate::debugger::context::Context; #[derive(Default)] pub struct CallStack { - /// Stack of indexes of sierra statements that are function calls and values of variables in frames corresponding to these functions. - /// Does ***not*** contain a current function frame. + /// Stack of function frames and values of variables in frames corresponding to these functions. + /// + /// Each substack contains zero to many frames of inlined functions together with exactly + /// one non-inlined function frame at the end of the substack. + /// Each substack corresponds to a function call statement that is currently on the call stack. + /// + /// Does ***not*** contain frames corresponding to the current statement. /// /// [Object references](https://microsoft.github.io/debug-adapter-protocol/overview#lifetime-of-objects-references): - /// object reference for each stack frame is equal to its `1 + 2 * index` where `index` is its - /// position in this vector. For variables, it is `2 + 2 * index`. - call_ids: Vec<(StatementIdx, FunctionVariables)>, + /// object reference for each stack frame is equal to its `1 + 2 * flat_index` + /// where `flat_index` is its position in the flattened vector. + /// For the variables' scope, the object reference is equal to `2 + 2 * flat_index`. + call_frames_and_vars: Vec>, /// Modification that should be applied to the stack when a new sierra statement is reached. /// @@ -29,14 +35,15 @@ pub struct CallStack { } enum Action { - Push(StatementIdx), + Push(Vec<(StackFrame, FunctionVariables)>), Pop, } impl CallStack { - pub fn depth(&self) -> usize { - self.call_ids.len() + pub fn depth(&self, statement_idx: StatementIdx, ctx: &Context) -> usize { + self.flat_length() + self.build_stack_frames(ctx, statement_idx).count() } + pub fn update(&mut self, statement_idx: StatementIdx, ctx: &Context) { // We can be sure that the `statement_idx` is different from the one which was the arg when // `action_on_new_statement` was set. @@ -44,32 +51,37 @@ impl CallStack { // https://github.com/starkware-libs/cairo/blob/20eca60c88a35f7da13f573b2fc68818506703a9/crates/cairo-lang-sierra-to-casm/src/invocations/function_call.rs#L46 // https://github.com/starkware-libs/cairo/blob/d52acf845fc234f1746f814de7c64b535563d479/crates/cairo-lang-sierra-to-casm/src/compiler.rs#L533 match self.action_on_new_statement.take() { - Some(Action::Push(statement)) => { + Some(Action::Push(frames_and_variables)) => { // TODO(#16) - self.call_ids.push((statement, FunctionVariables {})); + self.call_frames_and_vars.push(frames_and_variables); } Some(Action::Pop) => { - self.call_ids.pop(); + self.call_frames_and_vars.pop(); } None => {} } if ctx.is_function_call_statement(statement_idx) { - self.action_on_new_statement = Some(Action::Push(statement_idx)); + self.action_on_new_statement = Some(Action::Push( + self.build_stack_frames(ctx, statement_idx) + // TODO(#16) + .zip(iter::repeat_with(|| FunctionVariables {})) + .collect(), + )); } else if ctx.is_return_statement(statement_idx) { self.action_on_new_statement = Some(Action::Pop); } } pub fn get_frames(&self, statement_idx: StatementIdx, ctx: &Context) -> Vec { - // DAP expects frames to start from the most nested element. - self.call_ids + self.call_frames_and_vars .iter() - .map(|(call_statement_idx, _)| call_statement_idx) + .flatten() + .map(|(frame, _)| frame) .cloned() - .chain(once(statement_idx)) + .chain(self.build_stack_frames(ctx, statement_idx)) + // DAP expects frames to start from the most nested element. .rev() - .flat_map(|statement_idx| self.build_stack_frames(ctx, statement_idx)) .collect() } @@ -84,35 +96,43 @@ impl CallStack { } pub fn get_variables(&self, variables_reference: i64) -> Vec { - let index = variables_reference / 2 - 1; - let &FunctionVariables {} = if index == self.call_ids.len() as i64 { + let flat_index = (variables_reference / 2 - 1) as usize; + let &FunctionVariables {} = if flat_index >= self.flat_length() { // TODO(#16) // Build them on demand. &FunctionVariables {} } else { - &self.call_ids[index as usize].1 + self.call_frames_and_vars + .iter() + .flatten() + .map(|(_, vars)| vars) + .nth(flat_index) + .unwrap() }; vec![] } - /// Builds a vector of stack frames, ordered from the most nested (innermost) to the least nested (outermost) element. - fn build_stack_frames(&self, ctx: &Context, statement_idx: StatementIdx) -> Vec { + /// Builds a vector of stack frames, ordered from the least nested to the most nested element. + fn build_stack_frames<'a>( + &'a self, + ctx: &'a Context, + statement_idx: StatementIdx, + ) -> Box + 'a> { let Some(code_locations) = ctx.code_locations_for_statement_idx(statement_idx) else { - return vec![unknown_frame()]; + return Box::new(vec![self.unknown_frame()].into_iter()); }; - let default_function_names = vec![FunctionName("test".to_string())]; - let function_names = - ctx.function_names_for_statement_idx(statement_idx).unwrap_or(&default_function_names); + let function_names = ctx + .function_names_for_statement_idx(statement_idx) + .cloned() + .unwrap_or_else(|| vec![FunctionName("test".to_string())]); - code_locations - .iter() - .zip(function_names) - .map(|(code_location, function_name)| { - self.build_stack_frame(code_location, function_name, ctx) - }) - .collect() + Box::new(code_locations.clone().into_iter().rev().zip(function_names).map( + |(code_location, function_name)| { + self.build_stack_frame(&code_location, &function_name, ctx) + }, + )) } fn build_stack_frame( @@ -121,7 +141,6 @@ impl CallStack { FunctionName(function_name): &FunctionName, ctx: &Context, ) -> StackFrame { - let id = MIN_OBJECT_REFERENCE + 2 * self.call_ids.len() as i64; let file_path = Path::new(&source_file); let name = function_name.clone(); @@ -138,7 +157,7 @@ impl CallStack { let column = (code_span.start.col.0 + 1) as i64; StackFrame { - id, + id: self.next_frame_id(), name, source: Some(Source { name: None, @@ -151,16 +170,24 @@ impl CallStack { ..Default::default() } } -} -fn unknown_frame() -> StackFrame { - StackFrame { - id: 1, - name: "Unknown".to_string(), - line: 1, - column: 1, - presentation_hint: Some(StackFramePresentationhint::Subtle), - ..Default::default() + fn unknown_frame(&self) -> StackFrame { + StackFrame { + id: self.next_frame_id(), + name: "Unknown".to_string(), + line: 1, + column: 1, + presentation_hint: Some(StackFramePresentationhint::Subtle), + ..Default::default() + } + } + + fn next_frame_id(&self) -> i64 { + MIN_OBJECT_REFERENCE + 2 * self.flat_length() as i64 + } + + fn flat_length(&self) -> usize { + self.call_frames_and_vars.iter().map(|frames| frames.len()).sum() } } diff --git a/src/debugger/handler.rs b/src/debugger/handler.rs index 70cc77b..3feb789 100644 --- a/src/debugger/handler.rs +++ b/src/debugger/handler.rs @@ -185,8 +185,10 @@ pub fn handle_request( // This effectively "steps over" any function calls. let line = Line::create_from_statement_idx(state.current_statement_idx, ctx); - state.step_action = - Some(StepAction::Next { depth: state.call_stack.depth(), prev_line: line }); + state.step_action = Some(StepAction::Next { + depth: state.call_stack.depth(state.current_statement_idx, ctx), + prev_line: line, + }); state.resume_execution(); Ok(ResponseBody::Next.into()) @@ -208,7 +210,7 @@ pub fn handle_request( // We record the current call stack depth. The debugger will resume execution // and only stop when it reaches a line in a shallower call stack depth, which // happens when the current function returns. - let depth = state.call_stack.depth(); + let depth = state.call_stack.depth(state.current_statement_idx, ctx); if depth > 0 { state.step_action = Some(StepAction::StepOut { depth }); } From 1086f52160481925b977f6901b2a558295245147 Mon Sep 17 00:00:00 2001 From: piotmag769 Date: Tue, 10 Feb 2026 11:54:12 +0100 Subject: [PATCH 2/3] Vars supports --- Cargo.lock | 495 ++++++++++--------------------------- Cargo.toml | 6 +- src/debugger.rs | 34 +-- src/debugger/call_stack.rs | 41 ++- src/debugger/context.rs | 236 +++++++++++++++++- src/debugger/handler.rs | 9 +- src/debugger/state.rs | 3 +- 7 files changed, 426 insertions(+), 398 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e90361..b25f908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,12 +135,6 @@ dependencies = [ "cfg_aliases", ] -[[package]] -name = "boxcar" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" - [[package]] name = "bumpalo" version = "3.19.0" @@ -162,17 +156,17 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cairo-annotations" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97ec125673123e432c0082c80ceeeb2c18516d97373565f9710b19a7aafb6c" +source = "git+https://github.com/software-mansion/cairo-annotations?rev=796939a0b3cb57f3751835915509ef3b7093288c#796939a0b3cb57f3751835915509ef3b7093288c" dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-to-casm", + "cairo-lang-sierra-type-size", "camino", "derive_more", "regex", "serde", "serde_json", - "starknet-types-core", + "starknet-types-core 1.0.0", "strum", "strum_macros", "thiserror 2.0.17", @@ -184,20 +178,22 @@ version = "0.1.0" dependencies = [ "anyhow", "cairo-annotations", + "cairo-lang-casm", "cairo-lang-sierra", + "cairo-lang-sierra-to-casm", "cairo-vm", "dap", "scarb-metadata", "serde_json", - "starknet-types-core", + "starknet-types-core 0.2.4", "tracing", ] [[package]] name = "cairo-lang-casm" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2dcadc8a25455272821626d5a4704f5ff78e6338e2f2a51ac4424cd4c007da" +checksum = "7d1d84a85b59c753aa4a7f0c455a5c815e0aebb89faf0c8ab366b0d87c0bb934" dependencies = [ "cairo-lang-utils", "indoc", @@ -209,9 +205,9 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea391a08d10de26ddb3796d07c34f6685873534df7f0d55a3674500fecc9e53" +checksum = "ed04fc3f52d68157f359257c477e30f68dec36bbf568c85d567812583cd5f9c8" dependencies = [ "cairo-lang-utils", "good_lp", @@ -219,14 +215,14 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c76c30c4a8fbe235bc44c11a458fc6ad9a756245e11dbb6480b92db4b2987a" +checksum = "300655046f505cf806a918918e5397b20c22b579d78c2ef09bc7d4d59fd733be" dependencies = [ "anyhow", "cairo-lang-utils", "const-fnv1a-hash", - "convert_case 0.9.0", + "convert_case 0.8.0", "derivative", "itertools", "lalrpop", @@ -235,19 +231,20 @@ dependencies = [ "num-integer", "num-traits", "regex", + "rust-analyzer-salsa", "serde", "serde_json", "sha3", "smol_str", - "starknet-types-core", + "starknet-types-core 0.2.4", "thiserror 2.0.17", ] [[package]] name = "cairo-lang-sierra-ap-change" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e834cfadeda301c005acbcfd17e43b6515a85c7963d6e08e37668d6f6e5a1f" +checksum = "0c51190f463ac9f7d4a2ce0e0345cfc92334589811a7114eeeec84029999d7f1" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -261,9 +258,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69137101e55f87b5eb799a1ea6005577e952323564cb978baa80ed3e6a922b7f" +checksum = "bb0d0f038acd79aedcadad4ad2ad928b0881c4e96a2d9ad0e0b3173a6111f313" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -277,9 +274,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4563e2dbb25d2edeeb1cd12749fb22d3284c18cbbdb23f45032d984e805594" +checksum = "7c852277442b2d8ca9741cdc8ccb737c6ad381d300ab4e2d982a98ba40e5f5b6" dependencies = [ "assert_matches", "cairo-lang-casm", @@ -292,15 +289,15 @@ dependencies = [ "itertools", "num-bigint", "num-traits", - "starknet-types-core", + "starknet-types-core 0.2.4", "thiserror 2.0.17", ] [[package]] name = "cairo-lang-sierra-type-size" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d029f4226d5d5746c38194bd30928387c8d8ccd52099325233998e91f7254c4a" +checksum = "265aa8daaa94cc4d5e135a82c0bbe7d28d2c0fbc612332903dbf1a68ed15978f" dependencies = [ "cairo-lang-sierra", "cairo-lang-utils", @@ -308,23 +305,18 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "2.14.0" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e7dab0d9fb41e05d0dc3fdef607dfae2fe0e820d086f1d40303260cfdb86ec" +checksum = "cca315cce0937801a772bee5fe92cca28b8172421bdd2f67c96e8288a0dcfb9f" dependencies = [ - "hashbrown 0.16.1", - "indexmap", + "hashbrown 0.15.5", + "indexmap 2.12.1", "itertools", "num-bigint", "num-traits", - "salsa", "schemars", "serde", "smol_str", - "tracing", - "tracing-log", - "tracing-subscriber", - "vector-map", ] [[package]] @@ -354,7 +346,7 @@ dependencies = [ "sha2", "sha3", "starknet-crypto", - "starknet-types-core", + "starknet-types-core 0.2.4", "thiserror 2.0.17", "zip", ] @@ -408,9 +400,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.9.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db05ffb6856bf0ecdf6367558a76a0e8a77b1713044eb92845c692100ed50190" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -442,34 +434,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -507,15 +471,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - [[package]] name = "derivative" version = "2.2.0" @@ -540,18 +495,18 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case 0.10.0", "proc-macro2", @@ -627,12 +582,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "funty" version = "2.0.0" @@ -641,9 +590,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -684,6 +633,12 @@ dependencies = [ "microlp", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.5" @@ -692,7 +647,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.1.5", + "foldhash", "serde", ] @@ -701,22 +656,12 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", - "serde", - "serde_core", -] [[package]] -name = "hashlink" -version = "0.10.0" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -750,6 +695,17 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.12.1" @@ -771,24 +727,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "intrusive-collections" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" -dependencies = [ - "memoffset", -] - -[[package]] -name = "inventory" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" -dependencies = [ - "rustversion", -] - [[package]] name = "itertools" version = "0.14.0" @@ -922,15 +860,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - [[package]] name = "matrixmultiply" version = "0.3.10" @@ -947,15 +876,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "microlp" version = "0.2.11" @@ -1013,15 +933,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -1043,12 +954,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -1100,6 +1005,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -1158,7 +1069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.12.1", ] [[package]] @@ -1197,12 +1108,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1322,26 +1227,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -1351,26 +1236,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "regex" version = "1.12.2" @@ -1410,6 +1275,35 @@ dependencies = [ "subtle", ] +[[package]] +name = "rust-analyzer-salsa" +version = "0.17.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719825638c59fd26a55412a24561c7c5bcf54364c88b9a7a04ba08a6eafaba8d" +dependencies = [ + "indexmap 2.12.1", + "lock_api", + "oorandom", + "parking_lot", + "rust-analyzer-salsa-macros", + "rustc-hash", + "smallvec", + "tracing", + "triomphe", +] + +[[package]] +name = "rust-analyzer-salsa-macros" +version = "0.17.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d96498e9684848c6676c399032ebc37c52da95ecbefa83d71ccc53b9f8a4a8e" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "rust_decimal" version = "1.39.0" @@ -1422,9 +1316,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" @@ -1441,49 +1335,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "salsa" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27956164373aeec733ac24ff1736de8541234e3a8e7e6f916b28175b5752af3b" -dependencies = [ - "boxcar", - "crossbeam-queue", - "crossbeam-utils", - "hashbrown 0.15.5", - "hashlink", - "indexmap", - "intrusive-collections", - "inventory", - "parking_lot", - "portable-atomic", - "rayon", - "rustc-hash", - "salsa-macro-rules", - "salsa-macros", - "smallvec", - "thin-vec", - "tracing", -] - -[[package]] -name = "salsa-macro-rules" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ca3b9d6e47c08b5de4b218e0c5f7ec910b51bce6314e651c8e7b9d154d174da" - -[[package]] -name = "salsa-macros" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6337b62f2968be6b8afa30017d7564ecbde6832ada47ed2261fb14d0fd402ff4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", - "synstructure", -] - [[package]] name = "same-file" version = "1.0.6" @@ -1508,12 +1359,12 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", - "ref-cast", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -1521,9 +1372,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.1.0" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", @@ -1594,7 +1445,6 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap", "itoa", "memchr", "serde", @@ -1623,15 +1473,6 @@ dependencies = [ "keccak", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -1678,6 +1519,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "starknet-crypto" version = "0.8.1" @@ -1693,7 +1540,7 @@ dependencies = [ "rfc6979", "sha2", "starknet-curve", - "starknet-types-core", + "starknet-types-core 0.2.4", "zeroize", ] @@ -1703,7 +1550,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c898ae81b6409532374cf237f1bd752d068b96c6ad500af9ebbd0d9bb712f6" dependencies = [ - "starknet-types-core", + "starknet-types-core 0.2.4", ] [[package]] @@ -1726,6 +1573,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "starknet-types-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a12690813e587969cb4a9e7d8ebdb069d4bb7ec8d03275c5f719310c8e1f07c" +dependencies = [ + "generic-array", + "lambdaworks-crypto", + "lambdaworks-math", + "num-bigint", + "num-integer", + "num-traits", + "rand 0.9.2", + "serde", + "zeroize", +] + [[package]] name = "string_cache" version = "0.8.9" @@ -1750,7 +1614,7 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.106", @@ -1784,17 +1648,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "tap" version = "1.0.1" @@ -1810,12 +1663,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "thin-vec" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" - [[package]] name = "thiserror" version = "1.0.69" @@ -1856,46 +1703,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "toml_datetime" version = "0.7.3" @@ -1911,7 +1718,7 @@ version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ - "indexmap", + "indexmap 2.12.1", "toml_datetime", "toml_parser", "winnow", @@ -1955,37 +1762,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", ] [[package]] -name = "tracing-subscriber" -version = "0.3.22" +name = "triomphe" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "time", - "tracing", - "tracing-core", - "tracing-log", + "serde", + "stable_deref_trait", ] [[package]] @@ -2018,21 +1804,6 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vector-map" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b34e878e32c750bb4253be124adb9da1dc93ca5d98c210787badf1e1ccdca7" -dependencies = [ - "serde", -] - [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index 6492624..c017dc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,10 @@ edition = "2024" [dependencies] dap = { git = "https://github.com/software-mansion-labs/dap-rs", rev = "d26a3005fc37bb5127734dbfb0d8cadb6d5ca14b" } cairo-vm = { git = "https://github.com/software-mansion-labs/cairo-vm", rev = "4675b55df938162dbefb546242bafaf7d2d51efd", features = ["test_utils"] } -cairo-annotations = { version = "0.7.0", features = ["cairo-lang"] } -cairo-lang-sierra = "2.12.3" +cairo-annotations = { git = "https://github.com/software-mansion/cairo-annotations", rev = "796939a0b3cb57f3751835915509ef3b7093288c", features = ["cairo-lang"] } +cairo-lang-sierra = "=2.12.3" +cairo-lang-sierra-to-casm = "=2.12.3" +cairo-lang-casm = "=2.12.3" scarb-metadata = "1" serde_json = "1" tracing = "0.1" diff --git a/src/debugger.rs b/src/debugger.rs index 6a4172e..8610760 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -49,7 +49,7 @@ impl CairoDebugger { while !self.state.is_configuration_done() { // TODO(#35) let request = self.connection.next_request()?; - self.process_request(request)?; + self.process_request(request, None)?; } Ok(()) @@ -58,31 +58,31 @@ impl CairoDebugger { fn sync_with_vm(&mut self, vm: &VirtualMachine) -> Result<()> { self.state.update_state(vm, &self.ctx); - self.maybe_handle_breakpoint_hit()?; - self.maybe_handle_step_action()?; + self.maybe_handle_breakpoint_hit(vm)?; + self.maybe_handle_step_action(vm)?; while let Some(request) = self.connection.try_next_request()? { - self.process_request(request)?; + self.process_request(request, Some(vm))?; if self.state.is_execution_stopped() { - self.process_until_resume()?; + self.process_until_resume(vm)?; } } Ok(()) } - fn process_until_resume(&mut self) -> Result<()> { + fn process_until_resume(&mut self, vm: &VirtualMachine) -> Result<()> { while self.state.is_execution_stopped() { let request = self.connection.next_request()?; - self.process_request(request)?; + self.process_request(request, Some(vm))?; } Ok(()) } - fn process_request(&mut self, request: Request) -> Result<()> { - let response = handler::handle_request(&request, &mut self.state, &self.ctx)?; + fn process_request(&mut self, request: Request, vm: Option<&VirtualMachine>) -> Result<()> { + let response = handler::handle_request(&request, &mut self.state, &self.ctx, vm)?; let disconnected = matches!(response.response_body, ResponseBody::Disconnect); if let Some(event) = response.event { @@ -98,7 +98,7 @@ impl CairoDebugger { Ok(()) } - fn maybe_handle_step_action(&mut self) -> Result<()> { + fn maybe_handle_step_action(&mut self, vm: &VirtualMachine) -> Result<()> { let current_line = Line::create_from_statement_idx(self.state.current_statement_idx, &self.ctx); @@ -122,21 +122,25 @@ impl CairoDebugger { if stop { self.state.step_action = None; - self.pause_and_process_requests(StoppedEventReason::Step)?; + self.pause_and_process_requests(StoppedEventReason::Step, vm)?; } Ok(()) } - fn maybe_handle_breakpoint_hit(&mut self) -> Result<()> { + fn maybe_handle_breakpoint_hit(&mut self, vm: &VirtualMachine) -> Result<()> { if self.state.was_breakpoint_hit(&self.ctx) { - self.pause_and_process_requests(StoppedEventReason::Breakpoint)?; + self.pause_and_process_requests(StoppedEventReason::Breakpoint, vm)?; } Ok(()) } - fn pause_and_process_requests(&mut self, reason: StoppedEventReason) -> Result<()> { + fn pause_and_process_requests( + &mut self, + reason: StoppedEventReason, + vm: &VirtualMachine, + ) -> Result<()> { self.state.stop_execution(); self.connection.send_event(Event::Stopped(StoppedEventBody { reason, @@ -149,7 +153,7 @@ impl CairoDebugger { preserve_focus_hint: None, text: None, }))?; - self.process_until_resume() + self.process_until_resume(vm) } } diff --git a/src/debugger/call_stack.rs b/src/debugger/call_stack.rs index 264c6b6..00a794b 100644 --- a/src/debugger/call_stack.rs +++ b/src/debugger/call_stack.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; use std::iter; use std::path::Path; use cairo_annotations::annotations::coverage::{CodeLocation, SourceFileFullPath}; use cairo_annotations::annotations::profiler::FunctionName; use cairo_lang_sierra::program::StatementIdx; +use cairo_vm::vm::vm_core::VirtualMachine; use dap::types::{Scope, ScopePresentationhint, StackFrame, Variable}; use dap::types::{Source, StackFramePresentationhint}; @@ -44,7 +46,7 @@ impl CallStack { self.flat_length() + self.build_stack_frames(ctx, statement_idx).count() } - pub fn update(&mut self, statement_idx: StatementIdx, ctx: &Context) { + pub fn update(&mut self, statement_idx: StatementIdx, ctx: &Context, _vm: &VirtualMachine) { // We can be sure that the `statement_idx` is different from the one which was the arg when // `action_on_new_statement` was set. // The reason is that both function call and return in sierra compile to one CASM instruction each. @@ -65,7 +67,8 @@ impl CallStack { self.action_on_new_statement = Some(Action::Push( self.build_stack_frames(ctx, statement_idx) // TODO(#16) - .zip(iter::repeat_with(|| FunctionVariables {})) + // ctx.get_values_of_variables(statement_idx, vm), + .zip(iter::repeat_with(FunctionVariables::default)) .collect(), )); } else if ctx.is_return_statement(statement_idx) { @@ -95,12 +98,18 @@ impl CallStack { vec![scope] } - pub fn get_variables(&self, variables_reference: i64) -> Vec { + pub fn get_variables( + &self, + variables_reference: i64, + statement_idx: StatementIdx, + ctx: &Context, + vm: &VirtualMachine, + ) -> Vec { let flat_index = (variables_reference / 2 - 1) as usize; - let &FunctionVariables {} = if flat_index >= self.flat_length() { - // TODO(#16) - // Build them on demand. - &FunctionVariables {} + + let names_to_values = if flat_index >= self.flat_length() { + // Build them on demand. + ctx.get_values_of_variables(statement_idx, vm) } else { self.call_frames_and_vars .iter() @@ -108,9 +117,19 @@ impl CallStack { .map(|(_, vars)| vars) .nth(flat_index) .unwrap() + .names_to_values + .clone() }; - vec![] + names_to_values + .into_iter() + .map(|(name, value)| Variable { + name, + value, + variables_reference: 0, + ..Default::default() + }) + .collect() } /// Builds a vector of stack frames, ordered from the least nested to the most nested element. @@ -191,5 +210,7 @@ impl CallStack { } } -// TODO(#16) -struct FunctionVariables {} +#[derive(Default)] +struct FunctionVariables { + names_to_values: HashMap, +} diff --git a/src/debugger/context.rs b/src/debugger/context.rs index fe4a598..64c262a 100644 --- a/src/debugger/context.rs +++ b/src/debugger/context.rs @@ -6,15 +6,31 @@ use std::path::{Path, PathBuf}; use anyhow::{Context as AnyhowContext, Result, anyhow}; use cairo_annotations::annotations::TryFromDebugInfo; use cairo_annotations::annotations::coverage::{ - CodeLocation, CoverageAnnotationsV1 as SierraCodeLocations, + CodeLocation, CoverageAnnotationsV1 as SierraCodeLocations, SourceCodeSpan, +}; +use cairo_annotations::annotations::debugger::{ + DebuggerAnnotationsV1 as FunctionsDebugInfo, SierraFunctionId, SierraVarId, }; use cairo_annotations::annotations::profiler::{ FunctionName, ProfilerAnnotationsV1 as SierraFunctionNames, }; +use cairo_lang_casm::cell_expression::CellExpression; +use cairo_lang_casm::operand::Register; use cairo_lang_sierra::extensions::core::{CoreConcreteLibfunc, CoreLibfunc, CoreType}; +use cairo_lang_sierra::ids::VarId; use cairo_lang_sierra::program::{Program, ProgramArtifact, Statement, StatementIdx}; use cairo_lang_sierra::program_registry::ProgramRegistry; +use cairo_lang_sierra_to_casm::compiler::{ + CairoProgramDebugInfo, SierraToCasmConfig, StatementKindDebugInfo, +}; +use cairo_lang_sierra_to_casm::metadata::calc_metadata; +use cairo_lang_sierra_to_casm::references::ReferenceExpression; +use cairo_vm::Felt252; +use cairo_vm::types::relocatable::MaybeRelocatable; +use cairo_vm::vm::vm_core::VirtualMachine; use scarb_metadata::MetadataCommand; +use starknet_types_core::felt::Felt; +use tracing::trace; #[cfg(feature = "dev")] mod readable_sierra_ids; @@ -22,16 +38,20 @@ mod readable_sierra_ids; /// Struct that holds all the initial data needed for the debugger during execution. pub struct Context { pub root_path: PathBuf, - casm_debug_info: CasmDebugInfo, code_locations: SierraCodeLocations, function_names: SierraFunctionNames, files_data: HashMap, program: Program, sierra_program_registry: ProgramRegistry, + cairo_var_map: CairoVarMap, + casm_offsets: CasmDebugInfo, #[cfg(feature = "dev")] labels: HashMap, } +type CairoVarMap = HashMap>; +type CairoVar = (String, SourceCodeSpan); + pub struct CasmDebugInfo { /// Sierra statement index -> start CASM bytecode offset pub statement_to_pc: Vec, @@ -61,7 +81,7 @@ impl Line { } impl Context { - pub fn new(sierra_path: &Path, casm_debug_info: CasmDebugInfo) -> Result { + pub fn new(sierra_path: &Path, casm_offsets: CasmDebugInfo) -> Result { let root_path = get_project_root_path(sierra_path)?; let content = fs::read_to_string(sierra_path).expect("Failed to load sierra file"); @@ -74,9 +94,21 @@ impl Context { let debug_info = sierra_program .debug_info .ok_or_else(|| anyhow!("debug_info must be present in compiled sierra"))?; + let code_locations = SierraCodeLocations::try_from_debug_info(&debug_info)?; + let files_data = build_file_locations_map(&casm_offsets, &code_locations); + + let functions_debug_info = FunctionsDebugInfo::try_from_debug_info(&debug_info)?; + + // Temporary to get casm debug info until it is returned by USC. + let casm_debug_info = compile_sierra_to_get_casm_debug_info(&program)?; + let cairo_var_map = + build_cairo_var_to_casm_map(&program, &casm_debug_info, functions_debug_info); + eprintln!("{:#?}", cairo_var_map); + let function_names = SierraFunctionNames::try_from_debug_info(&debug_info)?; - let files_data = build_file_locations_map(&casm_debug_info, &code_locations); + + eprintln!("{}", program); Ok(Self { #[cfg(feature = "dev")] @@ -85,16 +117,17 @@ impl Context { root_path, code_locations, function_names, - casm_debug_info, files_data, program, sierra_program_registry, + cairo_var_map, + casm_offsets, }) } pub fn statement_idx_for_pc(&self, pc: usize) -> StatementIdx { StatementIdx( - self.casm_debug_info + self.casm_offsets .statement_to_pc .partition_point(|&offset| offset <= pc) .saturating_sub(1), @@ -168,6 +201,135 @@ impl Context { eprintln!("{with_labels}") } + + pub fn get_values_of_variables( + &self, + current_statement_idx: StatementIdx, + vm: &VirtualMachine, + ) -> HashMap { + // TODO: Use previous idx from THIS function to determine what statements need to be checked. + // Maybe store it in the call stack? + // For now we always analyze the function from the beginning to current statement. + + // TODO: The approach described above may be tricky for recursive functions (e.g. loops). + // Check the length of a stack in such case. + let function_entrypoint = &self.program.funcs[self + .program + .funcs + .partition_point(|x| x.entry_point.0 <= current_statement_idx.0) + - 1] + .entry_point; + + let mut current_var_values: HashMap)> = + HashMap::new(); + + for idx in function_entrypoint.0..=current_statement_idx.0 { + let Some(variables) = self.cairo_var_map.get(&StatementIdx(idx)) else { continue }; + + for ((name, span), (var_id, ref_expr)) in variables { + let cells = &ref_expr.cells; + let mut cells_vals = vec![]; + for cell in cells { + match cell { + CellExpression::Deref(cell_ref) => { + let mut relocatable = match cell_ref.register { + Register::AP => vm.get_ap(), + Register::FP => vm.get_fp(), + }; + let offset_from_register = cell_ref.offset as isize; + let register_offset = relocatable.offset as isize; + relocatable.offset = + (register_offset + offset_from_register).try_into().unwrap(); + + match vm.segments.memory.get_maybe_relocatable(relocatable) { + Ok(MaybeRelocatable::Int(value)) => cells_vals.push(value), + Ok(MaybeRelocatable::RelocatableValue(relocatable)) => { + trace!("UNEXPECTED RELOCATABLE (MAYBE ARRAY): {relocatable:?}") + } + Err(_) => (), + } + } + CellExpression::DoubleDeref(..) => { + trace!("DOUBLE Ds") + } + CellExpression::Immediate(value) => cells_vals.push(Felt::from(value)), + CellExpression::BinOp { .. } => { + trace!("BINOP") + } + }; + } + + if let Some((curr_span, _, _)) = current_var_values.get(name) { + // If there is a var with the same name in the map already, + // and it is further in the code, ignore the current var. + if span.start.line < curr_span.start.line + || (span.start.line == curr_span.start.line + && span.start.col < curr_span.start.col) + { + continue; + } + } + + if cells_vals.is_empty() { + continue; + } + + current_var_values.insert(name.clone(), (span.clone(), var_id.clone(), cells_vals)); + } + + // // Marks consumed vars as invalid in case of an invocation. + // match &self.program.statements[idx] { + // Statement::Invocation(invocation) => { + // match self.sierra_program_registry.get_libfunc(&invocation.libfunc_id).unwrap() + // { + // // Ignore `drop` since it does not consume the var at Cairo level. + // CoreConcreteLibfunc::Drop(_) => {} + // _ => { + // let dummy = Vec::new(); + // let vars_to_preserve = if let [ + // GenBranchInfo { target: GenBranchTarget::Fallthrough, results }, + // ] = invocation.branches.as_slice() + // { + // results + // } else { + // &dummy + // }; + // + // for var_id in + // invocation.args.iter().filter(|var| !vars_to_preserve.contains(var)) + // { + // if let Some(name_to_remove) = current_var_values + // .iter() + // .find(|(_, (_, id, _))| id.id == var_id.id) + // .map(|(name, _)| name.clone()) + // { + // current_var_values.remove(&name_to_remove); + // } + // } + // } + // } + // } + // Statement::Return(_) => {} + // } + } + + #[cfg(feature = "dev")] + self.print_statement(current_statement_idx); + // eprintln!("{current_statement_idx:?}: {current_var_values:#?}"); + eprintln!(); + + current_var_values + .into_iter() + .filter_map(|(name, (loc, var_id, value_in_felts))| { + if value_in_felts.len() == 1 { + Some((name, value_in_felts[0].to_string())) + } else { + eprintln!("UNSUPPORTED VALUE: ({name}, {loc:?}) {var_id:?} {value_in_felts:?}"); + None + } + }) + .collect() + } } fn build_file_locations_map( @@ -221,6 +383,68 @@ fn build_file_locations_map( file_map } +fn build_cairo_var_to_casm_map( + program: &Program, + cairo_program_debug_info: &CairoProgramDebugInfo, + functions_debug_info: FunctionsDebugInfo, +) -> CairoVarMap { + let mut cairo_var_to_ref_expr = HashMap::new(); + for (idx, statement_debug_info) in + cairo_program_debug_info.sierra_statement_info.iter().enumerate() + { + let (casm_ref_expressions_for_vars, vars) = + match (&program.statements[idx], &statement_debug_info.additional_kind_info) { + ( + Statement::Invocation(invocation), + StatementKindDebugInfo::Invoke(invocation_debug), + ) => { + let casm_ref_expressions_for_vars: Vec<_> = + invocation_debug.ref_values.iter().cloned().map(|x| x.expression).collect(); + (casm_ref_expressions_for_vars, invocation.args.clone()) + } + (Statement::Return(vars), StatementKindDebugInfo::Return(return_debug)) => { + let casm_ref_expressions_for_vars: Vec<_> = + return_debug.ref_values.iter().cloned().map(|x| x.expression).collect(); + (casm_ref_expressions_for_vars, vars.clone()) + } + _ => unreachable!(), + }; + + assert_eq!(casm_ref_expressions_for_vars.len(), vars.len()); + let function_id = + &program.funcs[program.funcs.partition_point(|x| x.entry_point.0 <= idx) - 1].id; + let func_debug_info = + &functions_debug_info.functions_info[&SierraFunctionId(function_id.id)]; + + for (casm_expressions, var_id) in casm_ref_expressions_for_vars.iter().zip(vars.clone()) { + let Some((name, span)) = + func_debug_info.sierra_to_cairo_variable.get(&SierraVarId(var_id.id)) + else { + continue; + }; + cairo_var_to_ref_expr + .entry(StatementIdx(idx)) + .or_insert_with(HashMap::new) + .insert((name.clone(), span.clone()), (var_id, casm_expressions.clone())); + } + } + + cairo_var_to_ref_expr +} + +fn compile_sierra_to_get_casm_debug_info(program: &Program) -> Result { + let metadata = calc_metadata(program, Default::default()) + .with_context(|| "Failed calculating metadata.")?; + let cairo_program = cairo_lang_sierra_to_casm::compiler::compile( + program, + &metadata, + SierraToCasmConfig { gas_usage_check: true, max_bytecode_size: usize::MAX }, + ) + .with_context(|| "Compilation failed.")?; + + Ok(cairo_program.debug_info) +} + // TODO(#50) fn get_project_root_path(sierra_path: &Path) -> Result { Ok(MetadataCommand::new() diff --git a/src/debugger/handler.rs b/src/debugger/handler.rs index 3feb789..2656891 100644 --- a/src/debugger/handler.rs +++ b/src/debugger/handler.rs @@ -1,4 +1,5 @@ use anyhow::{Result, anyhow, bail}; +use cairo_vm::vm::vm_core::VirtualMachine; use dap::events::{Event, StoppedEventBody}; use dap::prelude::{Command, Request, ResponseBody}; use dap::requests::{NextArguments, StepInArguments}; @@ -43,6 +44,7 @@ pub fn handle_request( request: &Request, state: &mut State, ctx: &Context, + vm: Option<&VirtualMachine>, ) -> Result { match &request.command { // We have not yet decided if we want to support these. @@ -172,7 +174,12 @@ pub fn handle_request( Ok(ResponseBody::Scopes(ScopesResponse { scopes }).into()) } Command::Variables(VariablesArguments { variables_reference, .. }) => { - let variables = state.call_stack.get_variables(*variables_reference); + let variables = state.call_stack.get_variables( + *variables_reference, + state.current_statement_idx, + ctx, + vm.unwrap(), + ); Ok(ResponseBody::Variables(VariablesResponse { variables }).into()) } diff --git a/src/debugger/state.rs b/src/debugger/state.rs index c72346a..3d4e613 100644 --- a/src/debugger/state.rs +++ b/src/debugger/state.rs @@ -1,4 +1,3 @@ -use std::cmp::PartialEq; use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -48,7 +47,7 @@ impl State { } self.current_statement_idx = ctx.statement_idx_for_pc(current_pc.offset); - self.call_stack.update(self.current_statement_idx, ctx); + self.call_stack.update(self.current_statement_idx, ctx, vm); } pub fn is_configuration_done(&self) -> bool { From 640aba6aa87cd7d438b0cf66498a719edb50deaf Mon Sep 17 00:00:00 2001 From: piotmag769 Date: Tue, 17 Feb 2026 16:25:26 +0100 Subject: [PATCH 3/3] idk xD --- src/debugger.rs | 34 +++++++++---------- src/debugger/call_stack.rs | 67 +++++++++++++++++++++++--------------- src/debugger/context.rs | 46 ++++++++++---------------- src/debugger/handler.rs | 9 +---- src/debugger/state.rs | 1 + 5 files changed, 76 insertions(+), 81 deletions(-) diff --git a/src/debugger.rs b/src/debugger.rs index 8610760..6a4172e 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -49,7 +49,7 @@ impl CairoDebugger { while !self.state.is_configuration_done() { // TODO(#35) let request = self.connection.next_request()?; - self.process_request(request, None)?; + self.process_request(request)?; } Ok(()) @@ -58,31 +58,31 @@ impl CairoDebugger { fn sync_with_vm(&mut self, vm: &VirtualMachine) -> Result<()> { self.state.update_state(vm, &self.ctx); - self.maybe_handle_breakpoint_hit(vm)?; - self.maybe_handle_step_action(vm)?; + self.maybe_handle_breakpoint_hit()?; + self.maybe_handle_step_action()?; while let Some(request) = self.connection.try_next_request()? { - self.process_request(request, Some(vm))?; + self.process_request(request)?; if self.state.is_execution_stopped() { - self.process_until_resume(vm)?; + self.process_until_resume()?; } } Ok(()) } - fn process_until_resume(&mut self, vm: &VirtualMachine) -> Result<()> { + fn process_until_resume(&mut self) -> Result<()> { while self.state.is_execution_stopped() { let request = self.connection.next_request()?; - self.process_request(request, Some(vm))?; + self.process_request(request)?; } Ok(()) } - fn process_request(&mut self, request: Request, vm: Option<&VirtualMachine>) -> Result<()> { - let response = handler::handle_request(&request, &mut self.state, &self.ctx, vm)?; + fn process_request(&mut self, request: Request) -> Result<()> { + let response = handler::handle_request(&request, &mut self.state, &self.ctx)?; let disconnected = matches!(response.response_body, ResponseBody::Disconnect); if let Some(event) = response.event { @@ -98,7 +98,7 @@ impl CairoDebugger { Ok(()) } - fn maybe_handle_step_action(&mut self, vm: &VirtualMachine) -> Result<()> { + fn maybe_handle_step_action(&mut self) -> Result<()> { let current_line = Line::create_from_statement_idx(self.state.current_statement_idx, &self.ctx); @@ -122,25 +122,21 @@ impl CairoDebugger { if stop { self.state.step_action = None; - self.pause_and_process_requests(StoppedEventReason::Step, vm)?; + self.pause_and_process_requests(StoppedEventReason::Step)?; } Ok(()) } - fn maybe_handle_breakpoint_hit(&mut self, vm: &VirtualMachine) -> Result<()> { + fn maybe_handle_breakpoint_hit(&mut self) -> Result<()> { if self.state.was_breakpoint_hit(&self.ctx) { - self.pause_and_process_requests(StoppedEventReason::Breakpoint, vm)?; + self.pause_and_process_requests(StoppedEventReason::Breakpoint)?; } Ok(()) } - fn pause_and_process_requests( - &mut self, - reason: StoppedEventReason, - vm: &VirtualMachine, - ) -> Result<()> { + fn pause_and_process_requests(&mut self, reason: StoppedEventReason) -> Result<()> { self.state.stop_execution(); self.connection.send_event(Event::Stopped(StoppedEventBody { reason, @@ -153,7 +149,7 @@ impl CairoDebugger { preserve_focus_hint: None, text: None, }))?; - self.process_until_resume(vm) + self.process_until_resume() } } diff --git a/src/debugger/call_stack.rs b/src/debugger/call_stack.rs index 00a794b..4a29c15 100644 --- a/src/debugger/call_stack.rs +++ b/src/debugger/call_stack.rs @@ -2,9 +2,11 @@ use std::collections::HashMap; use std::iter; use std::path::Path; -use cairo_annotations::annotations::coverage::{CodeLocation, SourceFileFullPath}; +use cairo_annotations::annotations::coverage::{CodeLocation, SourceCodeSpan, SourceFileFullPath}; use cairo_annotations::annotations::profiler::FunctionName; +use cairo_lang_sierra::ids::VarId; use cairo_lang_sierra::program::StatementIdx; +use cairo_vm::Felt252; use cairo_vm::vm::vm_core::VirtualMachine; use dap::types::{Scope, ScopePresentationhint, StackFrame, Variable}; use dap::types::{Source, StackFramePresentationhint}; @@ -12,6 +14,8 @@ use dap::types::{Source, StackFramePresentationhint}; use crate::debugger::MIN_OBJECT_REFERENCE; use crate::debugger::context::Context; +pub type Wars = HashMap)>; + #[derive(Default)] pub struct CallStack { /// Stack of function frames and values of variables in frames corresponding to these functions. @@ -26,7 +30,7 @@ pub struct CallStack { /// object reference for each stack frame is equal to its `1 + 2 * flat_index` /// where `flat_index` is its position in the flattened vector. /// For the variables' scope, the object reference is equal to `2 + 2 * flat_index`. - call_frames_and_vars: Vec>, + call_frames_and_vars: Vec<(Vec<(StackFrame, Wars)>, StatementIdx)>, /// Modification that should be applied to the stack when a new sierra statement is reached. /// @@ -34,10 +38,12 @@ pub struct CallStack { /// statement maps to a function call or a return statement. /// The stack should be modified ***after*** such a statement is executed. action_on_new_statement: Option, + + last_hope_of_this_project: Option<(StatementIdx, Wars)>, } enum Action { - Push(Vec<(StackFrame, FunctionVariables)>), + Push((Vec<(StackFrame, Wars)>, StatementIdx)), Pop, } @@ -56,21 +62,24 @@ impl CallStack { Some(Action::Push(frames_and_variables)) => { // TODO(#16) self.call_frames_and_vars.push(frames_and_variables); + self.last_hope_of_this_project = None; } Some(Action::Pop) => { - self.call_frames_and_vars.pop(); + let (frames, last_idx) = self.call_frames_and_vars.pop().unwrap(); + self.last_hope_of_this_project = Some((last_idx, frames.last().unwrap().clone().1)); } None => {} } if ctx.is_function_call_statement(statement_idx) { - self.action_on_new_statement = Some(Action::Push( + self.action_on_new_statement = Some(Action::Push(( self.build_stack_frames(ctx, statement_idx) // TODO(#16) // ctx.get_values_of_variables(statement_idx, vm), - .zip(iter::repeat_with(FunctionVariables::default)) + .zip(iter::repeat_with(Default::default)) .collect(), - )); + statement_idx, + ))); } else if ctx.is_return_statement(statement_idx) { self.action_on_new_statement = Some(Action::Pop); } @@ -79,7 +88,7 @@ impl CallStack { pub fn get_frames(&self, statement_idx: StatementIdx, ctx: &Context) -> Vec { self.call_frames_and_vars .iter() - .flatten() + .flat_map(|x| &x.0) .map(|(frame, _)| frame) .cloned() .chain(self.build_stack_frames(ctx, statement_idx)) @@ -98,33 +107,44 @@ impl CallStack { vec![scope] } - pub fn get_variables( - &self, - variables_reference: i64, + pub fn set_variables_values( + &mut self, statement_idx: StatementIdx, ctx: &Context, vm: &VirtualMachine, - ) -> Vec { + ) { + let current_var_values = + ctx.get_values_of_variables(statement_idx, vm, &self.last_hope_of_this_project); + self.last_hope_of_this_project = Some((statement_idx, current_var_values)); + } + + pub fn get_variables(&self, variables_reference: i64) -> Vec { let flat_index = (variables_reference / 2 - 1) as usize; - let names_to_values = if flat_index >= self.flat_length() { + let current_var_values = if flat_index >= self.flat_length() { // Build them on demand. - ctx.get_values_of_variables(statement_idx, vm) + &self.last_hope_of_this_project.as_ref().unwrap().1 } else { self.call_frames_and_vars .iter() - .flatten() + .flat_map(|x| &x.0) .map(|(_, vars)| vars) .nth(flat_index) .unwrap() - .names_to_values - .clone() }; - names_to_values - .into_iter() + current_var_values + .iter() + .filter_map(|(name, (loc, var_id, value_in_felts))| { + if value_in_felts.len() == 1 { + Some((name, value_in_felts[0].to_string())) + } else { + eprintln!("UNSUPPORTED VALUE: ({name}, {loc:?}) {var_id:?} {value_in_felts:?}"); + None + } + }) .map(|(name, value)| Variable { - name, + name: name.to_string(), value, variables_reference: 0, ..Default::default() @@ -206,11 +226,6 @@ impl CallStack { } fn flat_length(&self) -> usize { - self.call_frames_and_vars.iter().map(|frames| frames.len()).sum() + self.call_frames_and_vars.iter().map(|frames| frames.0.len()).sum() } } - -#[derive(Default)] -struct FunctionVariables { - names_to_values: HashMap, -} diff --git a/src/debugger/context.rs b/src/debugger/context.rs index 64c262a..481b79b 100644 --- a/src/debugger/context.rs +++ b/src/debugger/context.rs @@ -32,6 +32,8 @@ use scarb_metadata::MetadataCommand; use starknet_types_core::felt::Felt; use tracing::trace; +use crate::debugger::call_stack::Wars; + #[cfg(feature = "dev")] mod readable_sierra_ids; @@ -206,24 +208,22 @@ impl Context { &self, current_statement_idx: StatementIdx, vm: &VirtualMachine, - ) -> HashMap { - // TODO: Use previous idx from THIS function to determine what statements need to be checked. - // Maybe store it in the call stack? - // For now we always analyze the function from the beginning to current statement. - - // TODO: The approach described above may be tricky for recursive functions (e.g. loops). - // Check the length of a stack in such case. - let function_entrypoint = &self.program.funcs[self - .program - .funcs - .partition_point(|x| x.entry_point.0 <= current_statement_idx.0) - - 1] - .entry_point; - - let mut current_var_values: HashMap)> = - HashMap::new(); - - for idx in function_entrypoint.0..=current_statement_idx.0 { + last_hope: &Option<(StatementIdx, Wars)>, + ) -> HashMap)> { + let (last_statement_idx_in_current_function, mut current_var_values) = + last_hope.clone().unwrap_or_else(|| { + ( + self.program.funcs[self + .program + .funcs + .partition_point(|x| x.entry_point.0 <= current_statement_idx.0) + - 1] + .entry_point, + Default::default(), + ) + }); + + for idx in last_statement_idx_in_current_function.0..=current_statement_idx.0 { let Some(variables) = self.cairo_var_map.get(&StatementIdx(idx)) else { continue }; for ((name, span), (var_id, ref_expr)) in variables { @@ -319,16 +319,6 @@ impl Context { eprintln!(); current_var_values - .into_iter() - .filter_map(|(name, (loc, var_id, value_in_felts))| { - if value_in_felts.len() == 1 { - Some((name, value_in_felts[0].to_string())) - } else { - eprintln!("UNSUPPORTED VALUE: ({name}, {loc:?}) {var_id:?} {value_in_felts:?}"); - None - } - }) - .collect() } } diff --git a/src/debugger/handler.rs b/src/debugger/handler.rs index 2656891..3feb789 100644 --- a/src/debugger/handler.rs +++ b/src/debugger/handler.rs @@ -1,5 +1,4 @@ use anyhow::{Result, anyhow, bail}; -use cairo_vm::vm::vm_core::VirtualMachine; use dap::events::{Event, StoppedEventBody}; use dap::prelude::{Command, Request, ResponseBody}; use dap::requests::{NextArguments, StepInArguments}; @@ -44,7 +43,6 @@ pub fn handle_request( request: &Request, state: &mut State, ctx: &Context, - vm: Option<&VirtualMachine>, ) -> Result { match &request.command { // We have not yet decided if we want to support these. @@ -174,12 +172,7 @@ pub fn handle_request( Ok(ResponseBody::Scopes(ScopesResponse { scopes }).into()) } Command::Variables(VariablesArguments { variables_reference, .. }) => { - let variables = state.call_stack.get_variables( - *variables_reference, - state.current_statement_idx, - ctx, - vm.unwrap(), - ); + let variables = state.call_stack.get_variables(*variables_reference); Ok(ResponseBody::Variables(VariablesResponse { variables }).into()) } diff --git a/src/debugger/state.rs b/src/debugger/state.rs index 3d4e613..8951b29 100644 --- a/src/debugger/state.rs +++ b/src/debugger/state.rs @@ -48,6 +48,7 @@ impl State { self.current_statement_idx = ctx.statement_idx_for_pc(current_pc.offset); self.call_stack.update(self.current_statement_idx, ctx, vm); + self.call_stack.set_variables_values(self.current_statement_idx, ctx, vm); } pub fn is_configuration_done(&self) -> bool {