From c4727535c90d3471343625116211c79b43ed9d11 Mon Sep 17 00:00:00 2001 From: piotmag769 Date: Mon, 13 Jul 2026 16:19:57 +0200 Subject: [PATCH] Support V2 annotations --- Cargo.lock | 35 +++++------------ Cargo.toml | 2 +- src/debugger/context.rs | 38 +++++++++++++++++-- src/debugger/context/variables.rs | 62 +++++++++++++++++++++++++------ 4 files changed, 96 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de75616..9cbcea7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,8 +151,8 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cairo-annotations" -version = "0.8.0" -source = "git+https://github.com/software-mansion/cairo-annotations?rev=ff307d9f0ab4c6514b90698e3ce29d91f8e0ce56#ff307d9f0ab4c6514b90698e3ce29d91f8e0ce56" +version = "0.10.0" +source = "git+https://github.com/software-mansion/cairo-annotations?rev=78735bade42b4a9cb5505a53736ed28002d9d151#78735bade42b4a9cb5505a53736ed28002d9d151" dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-to-casm", @@ -161,7 +161,7 @@ dependencies = [ "derive_more", "serde", "serde_json", - "starknet-types-core 1.0.0", + "starknet-types-core", "strum", "strum_macros", "thiserror 2.0.18", @@ -182,7 +182,7 @@ dependencies = [ "indexmap", "scarb-metadata", "serde_json", - "starknet-types-core 0.2.4", + "starknet-types-core", "tracing", ] @@ -232,7 +232,7 @@ dependencies = [ "serde_json", "sha3", "smol_str", - "starknet-types-core 0.2.4", + "starknet-types-core", "thiserror 2.0.18", ] @@ -285,7 +285,7 @@ dependencies = [ "itertools", "num-bigint", "num-traits", - "starknet-types-core 0.2.4", + "starknet-types-core", "thiserror 2.0.18", ] @@ -345,7 +345,7 @@ dependencies = [ "sha2", "sha3", "starknet-crypto", - "starknet-types-core 0.2.4", + "starknet-types-core", "thiserror 2.0.18", "tracing", "zip", @@ -1680,7 +1680,7 @@ dependencies = [ "rfc6979", "sha2", "starknet-curve", - "starknet-types-core 0.2.4", + "starknet-types-core", "zeroize", ] @@ -1690,7 +1690,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c898ae81b6409532374cf237f1bd752d068b96c6ad500af9ebbd0d9bb712f6" dependencies = [ - "starknet-types-core 0.2.4", + "starknet-types-core", ] [[package]] @@ -1713,23 +1713,6 @@ 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" diff --git a/Cargo.toml b/Cargo.toml index 663a606..3de09e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] anyhow = "1.0" -cairo-annotations = { git = "https://github.com/software-mansion/cairo-annotations", rev = "ff307d9f0ab4c6514b90698e3ce29d91f8e0ce56", features = ["cairo-lang"] } +cairo-annotations = { git = "https://github.com/software-mansion/cairo-annotations", rev = "78735bade42b4a9cb5505a53736ed28002d9d151", features = ["cairo-lang"] } cairo-lang-casm = "2.17.0" cairo-lang-sierra = "2.17.0" cairo-lang-sierra-to-casm = "2.17.0" diff --git a/src/debugger/context.rs b/src/debugger/context.rs index 2d63d64..ea86650 100644 --- a/src/debugger/context.rs +++ b/src/debugger/context.rs @@ -7,7 +7,9 @@ use cairo_annotations::annotations::TryFromDebugInfo; use cairo_annotations::annotations::coverage::{ CodeLocation, CoverageAnnotationsV1 as SierraCodeLocations, }; -use cairo_annotations::annotations::debugger::DebuggerAnnotationsV1 as FunctionsDebugInfo; +use cairo_annotations::annotations::debugger::{ + DebuggerAnnotationsV2 as FunctionsDebugInfo, FunctionDebugInfoV2, VersionedDebuggerAnnotations, +}; use cairo_annotations::annotations::profiler::{ FunctionName, ProfilerAnnotationsV1 as SierraFunctionNames, }; @@ -74,8 +76,11 @@ impl Context { let code_locations = SierraCodeLocations::try_from_debug_info(&debug_info) .context("statements code locations debug info is missing - enable generating it in your Scarb.toml")?; - let functions_debug_info = FunctionsDebugInfo::try_from_debug_info(&debug_info) - .context("functions debug info is missing - enable generating it in your Scarb.toml")?; + let versioned_functions_debug_info = VersionedDebuggerAnnotations::try_from_debug_info( + &debug_info, + ) + .context("functions debug info is missing - enable generating it in your Scarb.toml")?; + let functions_debug_info = into_functions_debug_info_v2(versioned_functions_debug_info); let function_names = SierraFunctionNames::try_from_debug_info(&debug_info).context( "statements functions debug info is missing - enable generating it in your Scarb.toml", )?; @@ -280,6 +285,33 @@ impl Context { } } +/// Normalizes versioned debugger annotations to the V2 shape, which the rest of the debugger +/// codebase treats as the canonical format. V1's single Cairo variable per Sierra variable is +/// a subset of V2's multimap, so a V1 payload is wrapped into single-element vectors. +fn into_functions_debug_info_v2(versioned: VersionedDebuggerAnnotations) -> FunctionsDebugInfo { + match versioned { + VersionedDebuggerAnnotations::V2(v2) => v2, + VersionedDebuggerAnnotations::V1(v1) => FunctionsDebugInfo { + functions_info: v1 + .functions_info + .into_iter() + .map(|(function_id, info)| { + let function_info = FunctionDebugInfoV2 { + function_file_path: info.function_file_path, + function_code_span: info.function_code_span, + sierra_to_cairo_variables: info + .sierra_to_cairo_variable + .into_iter() + .map(|(var_id, cairo_var)| (var_id, vec![cairo_var])) + .collect(), + }; + (function_id, function_info) + }) + .collect(), + }, + } +} + fn sierra_function_for_statement(statement_idx: usize, program: &Program) -> &Function { &program.funcs[program.funcs.partition_point(|x| x.entry_point.0 <= statement_idx) - 1] } diff --git a/src/debugger/context/variables.rs b/src/debugger/context/variables.rs index e15cf90..af7e88e 100644 --- a/src/debugger/context/variables.rs +++ b/src/debugger/context/variables.rs @@ -3,7 +3,7 @@ use std::fmt::{Debug, Formatter}; use cairo_annotations::annotations::coverage::SourceCodeSpan; use cairo_annotations::annotations::debugger::{ - DebuggerAnnotationsV1 as FunctionsDebugInfo, FunctionDebugInfo, SierraFunctionId, SierraVarId, + DebuggerAnnotationsV2 as FunctionsDebugInfo, SierraFunctionId, SierraVarId, }; use cairo_lang_sierra::ids::VarId; use cairo_lang_sierra::program::{GenBranchTarget, Program, Statement, StatementIdx}; @@ -64,6 +64,22 @@ pub fn build_cairo_var_to_casm_map( functions_debug_info: FunctionsDebugInfo, ) -> HashMap { let mut result = HashMap::new(); + + // The iterator here is a deliberate choice - check doc comment of `extract_cairo_var_map`. + let mut functions_sierra_to_cairo_vars: HashMap<_, HashMap<_, std::slice::Iter<_>>> = + functions_debug_info + .functions_info + .iter() + .map(|(function_id, func_debug_info)| { + let sierra_to_cairo_var_map: HashMap<_, std::slice::Iter<_>> = func_debug_info + .sierra_to_cairo_variables + .iter() + .map(|(sierra_id, cairo_vars)| (sierra_id, cairo_vars.iter())) + .collect(); + (function_id, sierra_to_cairo_var_map) + }) + .collect(); + for (idx, statement_debug_info) in casm_debug_info.sierra_statement_info.iter().enumerate() { // Join information from casm debug info and sierra program to get casm reference for each // sierra var id. This is implemented as collecting vectors of `CairoVarReference`. @@ -121,14 +137,23 @@ pub fn build_cairo_var_to_casm_map( }; let function_id = &sierra_function_for_statement(idx, program).id; - let func_debug_info = - &functions_debug_info.functions_info[&SierraFunctionId(function_id.id)]; - - let consumed = extract_cairo_var_map(consumed, func_debug_info); + let Some(func_sierra_to_cairo_vars) = + functions_sierra_to_cairo_vars.get_mut(&SierraFunctionId(function_id.id)) + else { + // TODO: fix in the compiler + eprintln!( + "function {} should be present in the variable map", + function_id.debug_name.as_deref().unwrap_or_default() + ); + continue; + }; + + let consumed = extract_cairo_var_map(consumed, func_sierra_to_cairo_vars); let produced: HashMap<_, _> = produced .into_iter() .map(|(branch_target, cairo_var_refs)| { - let produced_in_branch = extract_cairo_var_map(cairo_var_refs, func_debug_info); + let produced_in_branch = + extract_cairo_var_map(cairo_var_refs, func_sierra_to_cairo_vars); (branch_target, produced_in_branch) }) .collect(); @@ -141,17 +166,32 @@ pub fn build_cairo_var_to_casm_map( result } -/// For each var reference use its sierra var id to get the Cairo variable it corresponds to. +/// For each var reference use its Sierra var id to get the Cairo variable it corresponds to. +/// +/// A single sierra var id can map to multiple Cairo variables (e.g, when its id is reused for a +/// rebinding such as `let y = x;`). +/// That's why values in `func_sierra_to_cairo_vars` are iterators. At the caller site sierra +/// statements are processed in the same order debug info is produced +/// (by increasing [`StatementIdx`]). Therefore, each time we extract the Cairo var definition +/// from the map, we consume it, which ensures that the reused sierra var ids in following sierra +/// statements are mapped to correct Cairo var definitions. +/// TODO: add an example. fn extract_cairo_var_map( var_refs: Vec, - func_debug_info: &FunctionDebugInfo, + func_sierra_to_cairo_vars: &mut HashMap< + &SierraVarId, + std::slice::Iter<(String, SourceCodeSpan)>, + >, ) -> HashMap { var_refs .into_iter() .filter_map(|var_ref| { - let (name, span) = - func_debug_info.sierra_to_cairo_variable.get(&SierraVarId(var_ref.sierra_id.id))?; - let var_id = CairoVarId { name: name.clone(), definition_span: span.clone() }; + let (name, definition_span) = func_sierra_to_cairo_vars + .get_mut(&SierraVarId(var_ref.sierra_id.id))? + .next()? + .clone(); + + let var_id = CairoVarId { name, definition_span }; Some((var_id, var_ref)) })