Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 9 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
38 changes: 35 additions & 3 deletions src/debugger/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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",
)?;
Expand Down Expand Up @@ -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]
}
Expand Down
62 changes: 51 additions & 11 deletions src/debugger/context/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -64,6 +64,22 @@ pub fn build_cairo_var_to_casm_map(
functions_debug_info: FunctionsDebugInfo,
) -> HashMap<StatementIdx, CairoVarsInStatement> {
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`.
Expand Down Expand Up @@ -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();
Expand All @@ -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<CairoVarReference>,
func_debug_info: &FunctionDebugInfo,
func_sierra_to_cairo_vars: &mut HashMap<
&SierraVarId,
std::slice::Iter<(String, SourceCodeSpan)>,
>,
) -> HashMap<CairoVarId, CairoVarReference> {
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))
})
Expand Down