From be37e65ad69541a277e4c5402d2b478905a66136 Mon Sep 17 00:00:00 2001 From: piotmag769 Date: Tue, 10 Feb 2026 09:08:26 +0100 Subject: [PATCH] Make sierra ids human readable for easier development --- .cargo/config.toml | 2 +- .github/workflows/ci.yml | 1 + .gitignore | 1 + Cargo.toml | 3 + src/debugger/context.rs | 19 ++++++ src/debugger/context/readable_sierra_ids.rs | 64 +++++++++++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/debugger/context/readable_sierra_ids.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index a2b3852..d4df3cd 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ [alias] -lint = "clippy --workspace --all-targets --all-features -- --no-deps" +lint = "all-features clippy --workspace --all-targets -- --no-deps" docs = "doc --workspace --all-features --no-deps" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bf4f97..d8cca03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 - run: cargo fmt --check + - run: cargo install cargo-all-features - run: cargo lint env: # Make sure CI fails on all warnings, including Clippy lints. diff --git a/.gitignore b/.gitignore index 5bcd307..f04a05d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ .idea/ .spr.yml +.DS_Store diff --git a/Cargo.toml b/Cargo.toml index b889ed6..6492624 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,6 @@ serde_json = "1" tracing = "0.1" anyhow = "1.0" starknet-types-core = "0.2.4" + +[features] +dev = [] diff --git a/src/debugger/context.rs b/src/debugger/context.rs index 6ad1c66..fe4a598 100644 --- a/src/debugger/context.rs +++ b/src/debugger/context.rs @@ -16,6 +16,9 @@ use cairo_lang_sierra::program::{Program, ProgramArtifact, Statement, StatementI use cairo_lang_sierra::program_registry::ProgramRegistry; use scarb_metadata::MetadataCommand; +#[cfg(feature = "dev")] +mod readable_sierra_ids; + /// Struct that holds all the initial data needed for the debugger during execution. pub struct Context { pub root_path: PathBuf, @@ -25,6 +28,8 @@ pub struct Context { files_data: HashMap, program: Program, sierra_program_registry: ProgramRegistry, + #[cfg(feature = "dev")] + labels: HashMap, } pub struct CasmDebugInfo { @@ -74,6 +79,9 @@ impl Context { let files_data = build_file_locations_map(&casm_debug_info, &code_locations); Ok(Self { + #[cfg(feature = "dev")] + labels: readable_sierra_ids::extract_labels(&program), + root_path, code_locations, function_names, @@ -149,6 +157,17 @@ impl Context { fn statement_idx_to_statement(&self, statement_idx: StatementIdx) -> &Statement { &self.program.statements[statement_idx.0] } + + #[cfg(feature = "dev")] + #[allow(unused)] + pub fn print_statement(&self, statement_idx: StatementIdx) { + let statement = self.statement_idx_to_statement(statement_idx); + let with_labels = readable_sierra_ids::replace_statement_id(statement.clone(), |idx| { + self.labels[&idx.0].clone() + }); + + eprintln!("{with_labels}") + } } fn build_file_locations_map( diff --git a/src/debugger/context/readable_sierra_ids.rs b/src/debugger/context/readable_sierra_ids.rs new file mode 100644 index 0000000..194f27d --- /dev/null +++ b/src/debugger/context/readable_sierra_ids.rs @@ -0,0 +1,64 @@ +use std::collections::{HashMap, HashSet}; + +use cairo_lang_sierra::program::{ + GenBranchInfo, GenBranchTarget, GenInvocation, GenStatement, Program, +}; + +// https://github.com/starkware-libs/cairo/blob/64b88f06c6261ac67c6b478434c844d4af81e5a3/crates/cairo-lang-sierra/src/fmt.rs#L29 +pub fn extract_labels(program: &Program) -> HashMap { + let funcs_labels = HashMap::::from_iter( + program.funcs.iter().enumerate().map(|(i, f)| (f.entry_point.0, format!("F{i}"))), + ); + // The offsets of branch targets. + let mut block_offsets = HashSet::::default(); + for s in &program.statements { + replace_statement_id(s.clone(), |idx| { + block_offsets.insert(idx.0); + }); + } + // All labels including inner function labels. + let mut labels = funcs_labels.clone(); + // Starting as `NONE` for support of invalid Sierra code. + let mut function_label = "NONE".to_string(); + // Assuming function code is contiguous - this is the index for same function labels. + let mut inner_idx = 0; + for i in 0..program.statements.len() { + if let Some(label) = funcs_labels.get(&i) { + function_label = label.clone(); + inner_idx = 0; + } else if block_offsets.contains(&i) { + labels.insert(i, format!("{function_label}_B{inner_idx}")); + inner_idx += 1; + } + } + + labels +} + +// https://github.com/starkware-libs/cairo/blob/c539d077479654eee6323d9c0c6eafad82d4851a/crates/cairo-lang-sierra/src/labeled_statement.rs#L25 +pub fn replace_statement_id( + statement: GenStatement, + mut map_stmt_id: impl FnMut(StatementIdIn) -> StatementIdOut, +) -> GenStatement { + match statement { + GenStatement::Invocation(GenInvocation { libfunc_id, args, branches }) => { + GenStatement::Invocation(GenInvocation { + libfunc_id, + args, + branches: branches + .into_iter() + .map(|GenBranchInfo { results, target }| GenBranchInfo { + results, + target: match target { + GenBranchTarget::Fallthrough => GenBranchTarget::Fallthrough, + GenBranchTarget::Statement(statement_id) => { + GenBranchTarget::Statement(map_stmt_id(statement_id)) + } + }, + }) + .collect(), + }) + } + GenStatement::Return(vars) => GenStatement::Return(vars), + } +}