From 9947290e2b505b406c888b058076dab49721b075 Mon Sep 17 00:00:00 2001 From: Ana Perez Ghiglia Date: Thu, 27 Feb 2025 17:19:04 -0300 Subject: [PATCH] adapt dap iterface to debug test functions --- tooling/debugger/src/dap.rs | 14 +++-- tooling/debugger/src/lib.rs | 9 +-- tooling/lsp/src/requests/code_lens_request.rs | 21 ++++++- tooling/nargo_cli/src/cli/dap_cmd.rs | 63 ++++++++++++++++--- tooling/nargo_cli/src/cli/debug_cmd.rs | 16 ++--- 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index f02be01b83a..45b7f883003 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; +use bn254_blackbox_solver::Bn254BlackBoxSolver; use nargo::PrintOutput; use crate::context::DebugContext; @@ -66,6 +67,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< initial_witness: WitnessMap, root_path: Option, package_name: String, + foreign_call_resolver_url: Option, ) -> Self { let context = DebugContext::new( solver, @@ -74,7 +76,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< initial_witness, Box::new(DefaultDebugForeignCallExecutor::from_artifact( PrintOutput::Stdout, - None, // TODO: set oracle_resolver url + foreign_call_resolver_url, debug_artifact, root_path, package_name, @@ -609,24 +611,28 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } } -pub fn run_session>( +pub fn run_session( server: Server, - solver: &B, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, + pedantic_solver: bool, + foreign_call_resolver_url: Option, ) -> Result<(), ServerError> { let debug_artifact = DebugArtifact { debug_symbols: program.debug.clone(), file_map: program.file_map.clone() }; + + let solver = Bn254BlackBoxSolver(pedantic_solver); let mut session = DapSession::new( server, - solver, + &solver, &program, &debug_artifact, initial_witness, root_path, package_name, + foreign_call_resolver_url, ); session.run_loop() diff --git a/tooling/debugger/src/lib.rs b/tooling/debugger/src/lib.rs index 09cbdb25353..b9a403a85d2 100644 --- a/tooling/debugger/src/lib.rs +++ b/tooling/debugger/src/lib.rs @@ -12,7 +12,7 @@ use std::path::PathBuf; use ::dap::errors::ServerError; use ::dap::server::Server; use acvm::acir::native_types::{WitnessMap, WitnessStack}; -use acvm::{BlackBoxFunctionSolver, FieldElement}; +use acvm::FieldElement; use nargo::NargoError; use noirc_driver::CompiledProgram; @@ -37,13 +37,14 @@ pub fn run_repl_session( ) } -pub fn run_dap_loop>( +pub fn run_dap_loop( server: Server, - solver: &B, program: CompiledProgram, initial_witness: WitnessMap, root_path: Option, package_name: String, + pedantic_solving: bool, + foreign_call_resolver_url: Option, ) -> Result<(), ServerError> { - dap::run_session(server, solver, program, initial_witness, root_path, package_name) + dap::run_session(server, program, initial_witness, root_path, package_name, pedantic_solving, foreign_call_resolver_url) } diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index ab98ab8bf10..55b01c197a5 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -23,6 +23,8 @@ const EXECUTE_COMMAND: &str = "nargo.execute"; const EXECUTE_CODELENS_TITLE: &str = "Execute"; const DEBUG_COMMAND: &str = "nargo.debug.dap"; const DEBUG_CODELENS_TITLE: &str = "Debug"; +const DEBUG_TEST_COMMAND: &str = "nargo.debug.test"; +const DEBUG_TEST_CODELENS_TITLE: &str = "Debug test"; fn with_arrow(title: &str) -> String { format!("{ARROW} {title}") @@ -117,7 +119,7 @@ pub(crate) fn collect_lenses_for_package( arguments: Some( [ package_selection_args(workspace, package), - vec!["--exact".into(), "--show-output".into(), func_name.into()], + vec!["--exact".into(), "--show-output".into(), func_name.clone().into()], ] .concat(), ), @@ -126,6 +128,23 @@ pub(crate) fn collect_lenses_for_package( let test_lens = CodeLens { range, command: Some(test_command), data: None }; lenses.push(test_lens); + + let debug_test_command = Command { + title: DEBUG_TEST_CODELENS_TITLE.to_string(), + command: DEBUG_TEST_COMMAND.into(), + arguments: Some( + [ + package_selection_args(workspace, package), + vec!["--exact".into(), func_name.into()], + ] + .concat(), + ), + }; + + let debug_test_lens = CodeLens { range, command: Some(debug_test_command), data: None }; + + lenses.push(debug_test_lens); + } if package.is_binary() { diff --git a/tooling/nargo_cli/src/cli/dap_cmd.rs b/tooling/nargo_cli/src/cli/dap_cmd.rs index 511f3abdb89..01d236f7832 100644 --- a/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,9 +1,9 @@ use acvm::acir::circuit::ExpressionWidth; use acvm::acir::native_types::WitnessMap; use acvm::FieldElement; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; +use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::Format; @@ -19,7 +19,8 @@ use dap::server::Server; use dap::types::Capabilities; use serde_json::Value; -use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging}; +use super::check_cmd::check_crate_and_report_errors; +use super::debug_cmd::{compile_bin_package_for_debugging, compile_options_for_debugging, compile_test_fn_for_debugging, get_test_function, load_workspace_files, prepare_package_for_debug}; use super::fs::inputs::read_inputs_from_file; use crate::errors::CliError; @@ -49,6 +50,9 @@ pub(crate) struct DapCommand { #[clap(long)] preflight_skip_instrumentation: bool, + #[clap(long)] + preflight_test_name: Option, + /// Use pedantic ACVM solving, i.e. double-check some black-box function /// assumptions when solving. /// This is disabled by default. @@ -99,6 +103,36 @@ fn workspace_not_found_error_msg(project_folder: &str, package: Option<&str>) -> } } +fn compile_main( + workspace: &Workspace, + package: &Package, + expression_width: ExpressionWidth, + compile_options: &CompileOptions, +) -> Result { + compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) + .map_err(|_| LoadError::Generic("Failed to compile project".into())) +} + +fn compile_test( + workspace: &Workspace, + package: &Package, + expression_width: ExpressionWidth, + compile_options: CompileOptions, + test_name: String, +) -> Result { + let (file_manager, mut parsed_files) = load_workspace_files(&workspace); + + let (mut context, crate_id) = + prepare_package_for_debug(&file_manager, &mut parsed_files, package, &workspace); + + check_crate_and_report_errors(&mut context, crate_id, &compile_options).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + let test = get_test_function(crate_id, &context, &test_name).map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + compile_test_fn_for_debugging(&test, &mut context, package, compile_options, Some(expression_width)).map_err(|_| LoadError::Generic("Failed to compile project".into())) + +} + fn load_and_compile_project( project_folder: &str, package: Option<&str>, @@ -106,19 +140,22 @@ fn load_and_compile_project( expression_width: ExpressionWidth, acir_mode: bool, skip_instrumentation: bool, + test_name: Option, ) -> Result<(CompiledProgram, WitnessMap, PathBuf, String), LoadError> { let workspace = find_workspace(project_folder, package) .ok_or(LoadError::Generic(workspace_not_found_error_msg(project_folder, package)))?; let package = workspace .into_iter() - .find(|p| p.is_binary()) - .ok_or(LoadError::Generic("No matching binary packages found in workspace".into()))?; + .find(|p| p.is_binary() || p.is_contract()) + .ok_or(LoadError::Generic("No matching binary or contract packages found in workspace. Only these packages can be debugged.".into()))?; let compile_options = compile_options_for_debugging(acir_mode, skip_instrumentation, CompileOptions::default()); - let compiled_program = - compile_bin_package_for_debugging(&workspace, package, &compile_options, expression_width) - .map_err(|_| LoadError::Generic("Failed to compile project".into()))?; + + let compiled_program = match test_name { + None => compile_main(&workspace, &package, expression_width, &compile_options), + Some(test_name) => compile_test(&workspace, &package, expression_width, compile_options, test_name), + }?; let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi) @@ -174,6 +211,12 @@ fn loop_uninitialized_dap( .get("skipInstrumentation") .and_then(|v| v.as_bool()) .unwrap_or(generate_acir); + let test_name = + additional_data.get("testName").and_then(|v| v.as_str()).map(String::from); + let oracle_resolver_url = additional_data + .get("oracleResolver") + .and_then(|v| v.as_str()) + .map(String::from); eprintln!("Project folder: {}", project_folder); eprintln!("Package: {}", package.unwrap_or("(default)")); @@ -186,17 +229,19 @@ fn loop_uninitialized_dap( expression_width, generate_acir, skip_instrumentation, + test_name, ) { Ok((compiled_program, initial_witness, root_path, package_name)) => { server.respond(req.ack()?)?; noir_debugger::run_dap_loop( server, - &Bn254BlackBoxSolver(pedantic_solving), compiled_program, initial_witness, Some(root_path), package_name, + pedantic_solving, + oracle_resolver_url, )?; break; } @@ -231,6 +276,7 @@ fn run_preflight_check( }; let package = args.preflight_package.as_deref(); + let test_name = args.preflight_test_name; let prover_name = args.preflight_prover_name.as_deref().unwrap_or(PROVER_INPUT_FILE); let _ = load_and_compile_project( @@ -240,6 +286,7 @@ fn run_preflight_check( expression_width, args.preflight_generate_acir, args.preflight_skip_instrumentation, + test_name, )?; Ok(()) diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 601738a5175..11b7602d373 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -162,7 +162,7 @@ fn debug_test_fn( compile_options: CompileOptions, run_params: RunParams, ) -> TestResult { - let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options); + let compiled_program = compile_test_fn_for_debugging(test, context, package, compile_options, None); let test_status = match compiled_program { Ok(compiled_program) => { @@ -199,16 +199,17 @@ fn debug_test_fn( ) } -fn compile_test_fn_for_debugging( +pub(super) fn compile_test_fn_for_debugging( test_def: &TestDefinition, context: &mut Context, package: &Package, compile_options: CompileOptions, + expression_with: Option, ) -> Result { let compiled_program = compile_no_check(context, &compile_options, test_def.function.get_id(), None, false)?; let expression_width = - get_target_width(package.expression_width, compile_options.expression_width); + expression_with.unwrap_or(get_target_width(package.expression_width, compile_options.expression_width)); let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); Ok(compiled_program) } @@ -321,18 +322,17 @@ fn debug_test( Ok(()) } -struct TestDefinition { +pub(super) struct TestDefinition { name: String, function: TestFunction, } // TODO: move to nargo::ops and reuse in test_cmd? -fn get_test_function( +pub(super) fn get_test_function( crate_id: CrateId, context: &Context, test_name: &str, ) -> Result { - // TODO: review Contains signature and check if its ok to send test_name as single element let test_pattern = FunctionNameMatch::Contains(vec![test_name.into()]); let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, &test_pattern); @@ -369,14 +369,14 @@ fn get_test_function( Ok(TestDefinition { name: test_name, function: test_function }) } -fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { +pub(super) fn load_workspace_files(workspace: &Workspace) -> (FileManager, ParsedFiles) { let mut file_manager = file_manager_with_stdlib(std::path::Path::new("")); insert_all_files_for_workspace_into_file_manager(workspace, &mut file_manager); let parsed_files = parse_all(&file_manager); (file_manager, parsed_files) } -pub(crate) fn prepare_package_for_debug<'a>( +pub(super) fn prepare_package_for_debug<'a>( file_manager: &'a FileManager, parsed_files: &'a mut ParsedFiles, package: &'a Package,