Skip to content
Merged
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
37 changes: 10 additions & 27 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 = { version = "1", 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
15 changes: 12 additions & 3 deletions src/debugger/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use cairo_lang_sierra::extensions::core::{CoreConcreteLibfunc, CoreTypeConcrete}
use cairo_lang_sierra::extensions::lib_func::BranchSignature;
use cairo_lang_sierra::extensions::types::TypeInfo;
use cairo_lang_sierra::extensions::{ConcreteLibfunc, ConcreteType};
use cairo_lang_sierra::ids::{ConcreteTypeId, VarId};
use cairo_lang_sierra::ids::{ConcreteTypeId, FunctionId, VarId};
use cairo_lang_sierra::program::{
Function, GenBranchTarget, GenInvocation, Program, ProgramArtifact, Statement, StatementIdx,
};
Expand All @@ -29,7 +29,9 @@ use cairo_lang_sierra_type_size::ProgramRegistryInfo;
use scarb_metadata::MetadataCommand;

use crate::debugger::context::file_locations::{FileCodeLocationsData, build_file_locations_map};
use crate::debugger::context::variables::build_cairo_var_to_casm_map;
use crate::debugger::context::variables::{
build_cairo_var_to_casm_map, build_function_to_param_vars_map,
};

mod file_locations;
#[cfg(feature = "dev")]
Expand All @@ -46,6 +48,7 @@ pub struct Context {
casm_debug_info: CairoProgramDebugInfo,
files_data: HashMap<PathBuf, FileCodeLocationsData>,
pub cairo_var_map: HashMap<StatementIdx, CairoVarsInStatement>,
pub function_param_var_map: HashMap<FunctionId, HashMap<CairoVarId, CairoVarReference>>,
#[cfg(feature = "dev")]
labels: HashMap<usize, String>,
}
Expand Down Expand Up @@ -85,7 +88,12 @@ impl Context {
let casm_debug_info =
compile_sierra_to_get_casm_debug_info(&program, &program_registry_info)?;
let cairo_var_map =
build_cairo_var_to_casm_map(&program, &casm_debug_info, functions_debug_info);
build_cairo_var_to_casm_map(&program, &casm_debug_info, &functions_debug_info);
let function_param_var_map = build_function_to_param_vars_map(
&program,
&program_registry_info.type_sizes,
&functions_debug_info,
);

let files_data = build_file_locations_map(&casm_debug_info, &code_locations);

Expand All @@ -109,6 +117,7 @@ impl Context {
casm_debug_info,
files_data,
cairo_var_map,
function_param_var_map,
})
}

Expand Down
95 changes: 92 additions & 3 deletions src/debugger/context/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ use cairo_annotations::annotations::coverage::SourceCodeSpan;
use cairo_annotations::annotations::debugger::{
DebuggerAnnotationsV1 as FunctionsDebugInfo, FunctionDebugInfo, SierraFunctionId, SierraVarId,
};
use cairo_lang_sierra::ids::VarId;
use cairo_lang_sierra::ids::{FunctionId, VarId};
use cairo_lang_sierra::program::{GenBranchTarget, Program, Statement, StatementIdx};
use cairo_lang_sierra_to_casm::compiler::{CairoProgramDebugInfo, StatementKindDebugInfo};
use cairo_lang_sierra_to_casm::references::ReferenceExpression;
use cairo_lang_sierra_to_casm::references::{ReferenceExpression, build_function_parameters_refs};
use cairo_lang_sierra_type_size::TypeSizeMap;

use crate::debugger::context::sierra_function_for_statement;

Expand Down Expand Up @@ -61,7 +62,7 @@ impl From<GenBranchTarget<StatementIdx>> for GenBranchTargetHashable {
pub fn build_cairo_var_to_casm_map(
program: &Program,
casm_debug_info: &CairoProgramDebugInfo,
functions_debug_info: FunctionsDebugInfo,
functions_debug_info: &FunctionsDebugInfo,
) -> HashMap<StatementIdx, CairoVarsInStatement> {
let mut result = HashMap::new();
for (idx, statement_debug_info) in casm_debug_info.sierra_statement_info.iter().enumerate() {
Expand Down Expand Up @@ -141,6 +142,94 @@ pub fn build_cairo_var_to_casm_map(
result
}

/// Build the per-function map of param name -> FP-relative reference.
///
/// The references retrieved in this function are FP-only.
/// They are independent of further usage of corresponding Sierra variables in the function body -
/// if a param is used in the function body, it is either consumed or copied to a new, AP-based cell, effectively becoming a local variable.
/// We don't handle the second case here, since we have a dedicated piece of logic for local variables that can shadow the references returned from here.
///
/// # Contract
/// We rely on CairoVM's calling convention to get the initial, FP-relative offsets of function params
/// and return references via FP.
pub fn build_function_to_param_vars_map(
program: &Program,
type_sizes: &TypeSizeMap,
functions_debug_info: &FunctionsDebugInfo,
) -> HashMap<FunctionId, HashMap<CairoVarId, CairoVarReference>> {
program
.funcs
.iter()
.map(|function| {
let debug_info =
&functions_debug_info.functions_info[&SierraFunctionId(function.id.id)];
let param_refs = build_function_parameters_refs(function, type_sizes)
.expect("function param refs construction should not fail");

let cairo_var_map = if let Some(parameters) = &debug_info.parameters {
parameters
.iter()
.filter_map(|(id, var_definition)| {
let sierra_id = VarId::new(id.0);
let ref_expr = param_refs.get(&sierra_id)?.expression.clone();
let cairo_var_ref = CairoVarReference { sierra_id, ref_expr };
let cairo_var_id = CairoVarId {
name: var_definition.name.clone(),
definition_span: var_definition.span.clone(),
};
Some((cairo_var_id, cairo_var_ref))
})
.collect()
} else {
// Fallback when `parameters` field is not present in debug info (Scarb < 2.20.0).
// We still know the `CairoVarReference` of the parameters - we try to find their
// `CairoVarId` in `sierra_to_cairo_variable` by their sierra var id.
//
// Note that this fallback works just as good as using the `parameters` field
// in the vast majority of cases. `parameters` works better for cases where
// sierra var ids are reused between params and local variables, such as:
//
// fn foo(x: felt252) {
// assert(true, '');
// // `x` has var id `0` - since it is a first param
// // The line below compiles to `store_temp([0]) -> ([0])`
// // so `y` has var id `0` as well.
// // This will cause `0: y` entry to override `0: x` entry in
// // `sierra_to_cairo_variable`.
// let mut y = x;
// y += 1;
// }
//
// This may lead to wrong variable names being shown for a while, since
// the parameter name we get from `sierra_to_cairo_variable` is in fact the local
// variable name. In the example above in mappings we will have sierra var id
// 0 mapped to `y`, so we will wrongly assume that the `x` param is named `y`.
// Assuming the function was called via `foo(5)`:
//
// fn foo(x: felt252) {
// assert(true, ''); // here `y = 5` will be shown instead of `x = 5`
// let mut y = x;
// y += 1;
// }
//
// We are fine with such errors - we prefer being wrong sometimes over not showing
// param values until their usage (after usage they become local vars and are shown
// anyways) for older Scarb version.
let param_references = param_refs
.into_iter()
.map(|(sierra_id, ref_value)| CairoVarReference {
sierra_id,
ref_expr: ref_value.expression,
})
.collect();
extract_cairo_var_map(param_references, debug_info)
};

(function.id.clone(), cairo_var_map)
})
.collect()
}

/// For each var reference use its sierra var id to get the Cairo variable it corresponds to.
fn extract_cairo_var_map(
var_refs: Vec<CairoVarReference>,
Expand Down
27 changes: 24 additions & 3 deletions src/debugger/state/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use indexmap::IndexMap;

use crate::debugger::MIN_OBJECT_REFERENCE;
use crate::debugger::context::Context;
use crate::debugger::state::call_stack::variables::get_values_of_variables;
use crate::debugger::state::call_stack::variables::{
get_values_of_function_params, get_values_of_variables,
};

mod variables;

Expand Down Expand Up @@ -181,11 +183,30 @@ impl CallStack {
ctx: &Context,
vm: &VirtualMachine,
) -> FunctionVariables {
get_values_of_variables(
let mut current_function_params = {
let last_executed_statement = self
.current_sierra_function_context
.last_executed_statement
.expect("last executed statement for current function should be set here - we are after pre-step update");
// Access to function params in VM's memory is based only on FP,
// which is fixed for each function execution (frame).
let registers_values = RegistersValues { ap: 0, fp: vm.get_fp().offset };

get_values_of_function_params(ctx, vm, last_executed_statement, &registers_values)
.unwrap_or_default()
};

let current_function_variables = get_values_of_variables(
ctx,
vm,
&self.current_sierra_function_context.post_statements_registers,
)
);

// Extend params with local variables so that local variables override params if there is
// a repetition (e.g. because a param variable changed its value).
current_function_params.names_to_values.extend(current_function_variables.names_to_values);

current_function_params
}

pub fn get_variables(
Expand Down
26 changes: 25 additions & 1 deletion src/debugger/state/call_stack/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ use cairo_lang_casm::cell_expression::CellExpression;
use cairo_lang_sierra::extensions::core::CoreTypeConcrete;
use cairo_lang_sierra::extensions::modules::starknet::StarknetTypeConcrete;
use cairo_lang_sierra::ids::ConcreteTypeId;
use cairo_lang_sierra::program::StatementIdx;
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
use cairo_vm::vm::vm_core::VirtualMachine;
use indexmap::IndexMap;
use tracing::warn;

use crate::debugger::context::{CairoVarId, CairoVarReference, Context};
use crate::debugger::state::call_stack::{FunctionVariables, PostStatementsRegisters};
use crate::debugger::state::call_stack::{
FunctionVariables, PostStatementsRegisters, RegistersValues,
};

mod type_name;
mod vm_reader;
Expand Down Expand Up @@ -117,6 +120,27 @@ pub fn get_values_of_variables(
FunctionVariables { names_to_values }
}

pub fn get_values_of_function_params(
ctx: &Context,
vm: &VirtualMachine,
last_executed_statement: StatementIdx,
registers_values: &RegistersValues,
) -> Option<FunctionVariables> {
let function = ctx.sierra_function_for_statement(last_executed_statement);
let params = ctx.function_param_var_map.get(&function.id)?;

let reader = VmReader::new(vm, registers_values);
let names_to_values: IndexMap<_, _> = params
.iter()
.filter_map(|(CairoVarId { name, .. }, CairoVarReference { sierra_id, ref_expr })| {
let type_id = &function.params.iter().find(|param| &param.id == sierra_id).unwrap().ty;
Some((name.to_string(), extract_var_value(&ref_expr.cells, type_id, &reader, ctx)?))
})
.collect();

Some(FunctionVariables { names_to_values })
}

fn extract_var_value(
cells: &[CellExpression],
type_id: &ConcreteTypeId,
Expand Down