1use crate::foreign_calls::DebugForeignCallExecutor;
2use acvm::acir::brillig::BitSize;
3use acvm::acir::circuit::brillig::{BrilligBytecode, BrilligFunctionId};
4use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation};
5use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack};
6use acvm::brillig_vm::MemoryValue;
7use acvm::pwg::{
8 ACVM, ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo,
9 OpcodeNotSolvable, StepResult,
10};
11use acvm::{BlackBoxFunctionSolver, FieldElement};
12
13use codespan_reporting::files::{Files, SimpleFile};
14use fm::FileId;
15use nargo::NargoError;
16use nargo::errors::{ExecutionError, Location, ResolvedOpcodeLocation, execution_error_from};
17use noirc_artifacts::debug::{DebugArtifact, StackFrame};
18use noirc_driver::{CompiledProgram, DebugFile};
19
20use noirc_errors::call_stack::CallStackId;
21use noirc_errors::debug_info::DebugInfo;
22use noirc_printable_type::{PrintableType, PrintableValue};
23use thiserror::Error;
24
25use std::collections::BTreeMap;
26use std::collections::HashSet;
27use std::path::PathBuf;
28
29#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
61pub struct AddressMap {
62 addresses: Vec<Vec<usize>>,
63
64 last_valid_address: usize,
66
67 brillig_addresses: Vec<BrilligAddressSpace>,
70}
71
72#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
76struct BrilligAddressSpace {
77 start_address: usize,
79 end_address: usize,
81 brillig_function_id: BrilligFunctionId,
83}
84
85impl AddressMap {
86 pub(super) fn new(
87 circuits: &[Circuit<FieldElement>],
88 unconstrained_functions: &[BrilligBytecode<FieldElement>],
89 ) -> Self {
90 let opcode_address_size = |opcode: &Opcode<FieldElement>| {
91 if let Opcode::BrilligCall { id, .. } = opcode {
92 (unconstrained_functions[id.as_usize()].bytecode.len(), Some(*id))
93 } else {
94 (1, None)
95 }
96 };
97
98 let mut addresses = Vec::with_capacity(circuits.len());
99 let mut next_address = 0usize;
100 let mut brillig_addresses = Vec::new();
101
102 for circuit in circuits {
103 let mut circuit_addresses = Vec::with_capacity(circuit.opcodes.len());
104 for opcode in &circuit.opcodes {
105 circuit_addresses.push(next_address);
106 let (address_size, brillig_function_id) = opcode_address_size(opcode);
107 if let Some(brillig_function_id) = brillig_function_id {
108 let brillig_address_space = BrilligAddressSpace {
109 start_address: next_address,
110 end_address: next_address + address_size,
111 brillig_function_id,
112 };
113 brillig_addresses.push(brillig_address_space);
114 }
115 next_address += address_size;
116 }
117 addresses.push(circuit_addresses);
118 }
119
120 Self { addresses, last_valid_address: next_address - 1, brillig_addresses }
121 }
122
123 pub fn debug_location_to_address(&self, location: &DebugLocation) -> usize {
127 let circuit_addresses = &self.addresses[location.circuit_id as usize];
128 match &location.opcode_location {
129 OpcodeLocation::Acir(acir_index) => circuit_addresses[*acir_index],
130 OpcodeLocation::Brillig { acir_index, brillig_index } => {
131 circuit_addresses[*acir_index] + *brillig_index
132 }
133 }
134 }
135
136 pub fn address_to_debug_location(&self, address: usize) -> Option<DebugLocation> {
137 if address > self.last_valid_address {
138 return None;
139 }
140 let circuit_id =
144 match self.addresses.binary_search_by(|addresses| addresses[0].cmp(&address)) {
145 Ok(found_index) => found_index,
146 Err(insert_index) => insert_index - 1,
149 };
150
151 let (opcode_location, brillig_function_id) =
155 match self.addresses[circuit_id].binary_search(&address) {
156 Ok(found_index) => (OpcodeLocation::Acir(found_index), None),
157 Err(insert_index) => {
158 let acir_index = insert_index - 1;
159 let base_offset = self.addresses[circuit_id][acir_index];
160 let brillig_index = address - base_offset;
161 let brillig_function_id = self
162 .brillig_addresses
163 .iter()
164 .find(|brillig_address_space| {
165 address >= brillig_address_space.start_address
166 && address <= brillig_address_space.end_address
167 })
168 .map(|brillig_address_space| brillig_address_space.brillig_function_id);
169 (OpcodeLocation::Brillig { acir_index, brillig_index }, brillig_function_id)
170 }
171 };
172
173 Some(DebugLocation { circuit_id: circuit_id as u32, opcode_location, brillig_function_id })
174 }
175}
176
177#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
178pub struct DebugLocation {
179 pub circuit_id: u32,
180 pub opcode_location: OpcodeLocation,
181 pub brillig_function_id: Option<BrilligFunctionId>,
182}
183
184impl std::fmt::Display for DebugLocation {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 let circuit_id = self.circuit_id;
187 match self.opcode_location {
188 OpcodeLocation::Acir(index) => write!(f, "{circuit_id}:{index}"),
189 OpcodeLocation::Brillig { acir_index, brillig_index } => {
190 write!(f, "{circuit_id}:{acir_index}.{brillig_index}")
191 }
192 }
193 }
194}
195
196impl From<DebugLocation> for ResolvedOpcodeLocation {
197 fn from(debug_loc: DebugLocation) -> Self {
198 ResolvedOpcodeLocation {
199 acir_function_index: usize::try_from(debug_loc.circuit_id).unwrap(),
200 opcode_location: debug_loc.opcode_location,
201 }
202 }
203}
204
205#[derive(Error, Debug)]
206pub enum DebugLocationFromStrError {
207 #[error("Invalid debug location string: {0}")]
208 InvalidDebugLocationString(String),
209}
210
211impl std::str::FromStr for DebugLocation {
212 type Err = DebugLocationFromStrError;
213 fn from_str(s: &str) -> Result<Self, Self::Err> {
214 let parts: Vec<_> = s.split(':').collect();
215 let error = Err(DebugLocationFromStrError::InvalidDebugLocationString(s.to_string()));
216
217 match parts.len() {
218 1 => OpcodeLocation::from_str(parts[0]).map_or(error, |opcode_location| {
219 Ok(DebugLocation { circuit_id: 0, opcode_location, brillig_function_id: None })
220 }),
221 2 => {
222 let first_part = parts[0].parse().ok();
223 let second_part = OpcodeLocation::from_str(parts[1]).ok();
224 if let (Some(circuit_id), Some(opcode_location)) = (first_part, second_part) {
225 Ok(DebugLocation { circuit_id, opcode_location, brillig_function_id: None })
226 } else {
227 error
228 }
229 }
230 _ => error,
231 }
232 }
233}
234
235#[derive(Debug)]
236pub(super) enum DebugCommandResult {
237 Done,
238 Ok,
239 BreakpointReached(DebugLocation),
240 Error(NargoError<FieldElement>),
241}
242
243#[derive(Debug)]
244pub struct DebugStackFrame<F> {
245 pub function_name: String,
246 pub function_params: Vec<String>,
247 pub variables: Vec<(String, PrintableValue<F>, PrintableType)>,
248}
249
250impl<F: Clone> From<&StackFrame<'_, F>> for DebugStackFrame<F> {
251 fn from(value: &StackFrame<F>) -> Self {
252 DebugStackFrame {
253 function_name: value.function_name.to_string(),
254 function_params: value.function_params.iter().map(|param| param.to_string()).collect(),
255 variables: value
256 .variables
257 .iter()
258 .map(|(name, value, var_type)| {
259 (name.to_string(), (**value).clone(), (*var_type).clone())
260 })
261 .collect(),
262 }
263 }
264}
265
266pub struct ExecutionFrame<'a, B: BlackBoxFunctionSolver<FieldElement>> {
267 circuit_id: u32,
268 acvm: ACVM<'a, FieldElement, B>,
269}
270
271#[derive(Debug)]
272pub enum DebugExecutionResult {
273 Solved(WitnessStack<FieldElement>),
274 Incomplete,
275 Error(NargoError<FieldElement>),
276}
277
278#[derive(Debug, Clone)]
279pub struct DebugProject {
280 pub compiled_program: CompiledProgram,
281 pub initial_witness: WitnessMap<FieldElement>,
282 pub root_dir: PathBuf,
283 pub package_name: String,
284}
285
286#[derive(Debug, Clone)]
287
288pub struct RunParams {
289 pub pedantic_solving: bool,
291
292 pub raw_source_printing: Option<bool>,
295
296 pub oracle_resolver_url: Option<String>,
298}
299
300pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver<FieldElement>> {
301 pub(crate) acvm: ACVM<'a, FieldElement, B>,
302 current_circuit_id: u32,
303 brillig_solver: Option<BrilligSolver<'a, FieldElement, B>>,
304
305 witness_stack: WitnessStack<FieldElement>,
306 acvm_stack: Vec<ExecutionFrame<'a, B>>,
307
308 backend: &'a B,
309 foreign_call_executor: Box<dyn DebugForeignCallExecutor + 'a>,
310
311 debug_artifact: &'a DebugArtifact,
312 breakpoints: HashSet<DebugLocation>,
313 source_to_locations: BTreeMap<FileId, Vec<(usize, DebugLocation)>>,
314
315 circuits: &'a [Circuit<FieldElement>],
316 unconstrained_functions: &'a [BrilligBytecode<FieldElement>],
317
318 acir_opcode_addresses: AddressMap,
319 initial_witness: WitnessMap<FieldElement>,
320}
321
322fn initialize_acvm<'a, B: BlackBoxFunctionSolver<FieldElement>>(
323 backend: &'a B,
324 circuits: &'a [Circuit<FieldElement>],
325 initial_witness: WitnessMap<FieldElement>,
326 unconstrained_functions: &'a [BrilligBytecode<FieldElement>],
327) -> ACVM<'a, FieldElement, B> {
328 let current_circuit_id: u32 = 0;
329 let initial_circuit = &circuits[current_circuit_id as usize];
330
331 ACVM::new(
332 backend,
333 &initial_circuit.opcodes,
334 initial_witness,
335 unconstrained_functions,
336 &initial_circuit.assert_messages,
337 )
338}
339
340impl<'a, B: BlackBoxFunctionSolver<FieldElement>> DebugContext<'a, B> {
341 pub(super) fn new(
342 blackbox_solver: &'a B,
343 circuits: &'a [Circuit<FieldElement>],
344 debug_artifact: &'a DebugArtifact,
345 initial_witness: WitnessMap<FieldElement>,
346 foreign_call_executor: Box<dyn DebugForeignCallExecutor + 'a>,
347 unconstrained_functions: &'a [BrilligBytecode<FieldElement>],
348 ) -> Self {
349 let source_to_opcodes = build_source_to_opcode_debug_mappings(debug_artifact);
350 let current_circuit_id: u32 = 0;
351 let acir_opcode_addresses = AddressMap::new(circuits, unconstrained_functions);
352 Self {
353 current_circuit_id,
354 brillig_solver: None,
355 witness_stack: WitnessStack::default(),
356 acvm_stack: vec![],
357 backend: blackbox_solver,
358 foreign_call_executor,
359 debug_artifact,
360 breakpoints: HashSet::new(),
361 source_to_locations: source_to_opcodes,
362 circuits,
363 unconstrained_functions,
364 acir_opcode_addresses,
365 initial_witness: initial_witness.clone(), acvm: initialize_acvm(
367 blackbox_solver,
368 circuits,
369 initial_witness,
370 unconstrained_functions,
371 ),
372 }
373 }
374
375 pub(super) fn get_opcodes(&self) -> &[Opcode<FieldElement>] {
376 self.acvm.opcodes()
377 }
378
379 pub(super) fn get_opcodes_of_circuit(&self, circuit_id: u32) -> &[Opcode<FieldElement>] {
380 &self.circuits[circuit_id as usize].opcodes
381 }
382
383 pub(super) fn get_witness_map(&self) -> &WitnessMap<FieldElement> {
384 self.acvm.witness_map()
385 }
386
387 pub(super) fn overwrite_witness(
388 &mut self,
389 witness: Witness,
390 value: FieldElement,
391 ) -> Option<FieldElement> {
392 self.acvm.overwrite_witness(witness, value)
393 }
394
395 pub(super) fn get_current_debug_location(&self) -> Option<DebugLocation> {
396 let ip = self.acvm.instruction_pointer();
397 if ip >= self.get_opcodes().len() {
398 None
399 } else {
400 let (opcode_location, brillig_function_id) =
401 if let Some(ref solver) = self.brillig_solver {
402 let function_id = solver.function_id;
403 (
404 OpcodeLocation::Brillig {
405 acir_index: ip,
406 brillig_index: solver.program_counter(),
407 },
408 Some(function_id),
409 )
410 } else {
411 (OpcodeLocation::Acir(ip), None)
412 };
413 Some(DebugLocation {
414 circuit_id: self.current_circuit_id,
415 brillig_function_id,
416 opcode_location,
417 })
418 }
419 }
420
421 pub(super) fn get_call_stack(&self) -> Vec<DebugLocation> {
422 let mut frames: Vec<_> = self
424 .acvm_stack
425 .iter()
426 .map(|ExecutionFrame { circuit_id, acvm }| DebugLocation {
427 circuit_id: *circuit_id,
428 opcode_location: OpcodeLocation::Acir(acvm.instruction_pointer()),
429 brillig_function_id: None,
430 })
431 .collect();
432
433 let instruction_pointer = self.acvm.instruction_pointer();
435 let circuit_id = self.current_circuit_id;
436 if let Some(ref solver) = self.brillig_solver {
437 frames.extend(solver.get_call_stack().iter().map(|program_counter| DebugLocation {
438 circuit_id,
439 opcode_location: OpcodeLocation::Brillig {
440 acir_index: instruction_pointer,
441 brillig_index: *program_counter,
442 },
443 brillig_function_id: Some(solver.function_id),
444 }));
445 } else if instruction_pointer < self.get_opcodes().len() {
446 frames.push(DebugLocation {
447 circuit_id,
448 opcode_location: OpcodeLocation::Acir(instruction_pointer),
449 brillig_function_id: None,
450 });
451 }
452 frames
453 }
454
455 pub(super) fn is_source_location_in_debug_module(&self, location: &Location) -> bool {
456 self.debug_artifact
457 .file_map
458 .get(&location.file)
459 .map(is_debug_file_in_debug_crate)
460 .unwrap_or(false)
461 }
462
463 pub(super) fn find_opcode_for_source_location(
476 &self,
477 file_id: &FileId,
478 line: i64,
479 ) -> Option<DebugLocation> {
480 let line = line as usize;
481 let line_to_opcodes = self.source_to_locations.get(file_id)?;
482 let found_location = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) {
483 Ok(index) => {
484 let mut index = index;
486 while index > 0 && line_to_opcodes[index - 1].0 == line {
487 index -= 1;
488 }
489 line_to_opcodes[index].1
490 }
491 Err(index) => {
492 if index >= line_to_opcodes.len() {
493 return None;
494 }
495 line_to_opcodes[index].1
496 }
497 };
498 Some(found_location)
499 }
500
501 pub(super) fn find_opcode_at_current_file_line(&self, line: i64) -> Option<DebugLocation> {
502 let file = self.get_current_file()?;
503
504 self.find_opcode_for_source_location(&file, line)
505 }
506
507 pub(super) fn get_current_source_location(&self) -> Option<Vec<Location>> {
515 self.get_current_debug_location()
516 .as_ref()
517 .map(|debug_location| self.get_source_location_for_debug_location(debug_location))
518 .filter(|v: &Vec<Location>| !v.is_empty())
519 }
520
521 fn get_current_file(&self) -> Option<FileId> {
523 self.get_current_source_location()
524 .and_then(|locations| locations.last().map(|location| location.file))
525 }
526
527 pub(super) fn get_source_location_for_debug_location(
534 &self,
535 debug_location: &DebugLocation,
536 ) -> Vec<Location> {
537 self.debug_artifact.debug_symbols[debug_location.circuit_id as usize]
538 .opcode_location(&debug_location.opcode_location)
539 .unwrap_or_else(|| {
540 if let (Some(brillig_function_id), Some(brillig_location)) = (
541 debug_location.brillig_function_id,
542 debug_location.opcode_location.to_brillig_location(),
543 ) {
544 let brillig_locations = self.debug_artifact.debug_symbols
545 [debug_location.circuit_id as usize]
546 .brillig_locations
547 .get(&brillig_function_id);
548 let call_stack_id = brillig_locations
549 .unwrap()
550 .get(&brillig_location)
551 .copied()
552 .unwrap_or_default();
553 self.debug_artifact.debug_symbols[debug_location.circuit_id as usize]
554 .location_tree
555 .get_call_stack(call_stack_id)
556 } else {
557 vec![]
558 }
559 })
560 .into_iter()
561 .filter(|source_location| !self.is_source_location_in_debug_module(source_location))
562 .collect()
563 }
564
565 pub(super) fn get_source_call_stack(&self) -> Vec<(DebugLocation, Location)> {
570 self.get_call_stack()
571 .iter()
572 .flat_map(|debug_location| {
573 self.get_source_location_for_debug_location(debug_location)
574 .into_iter()
575 .map(|source_location| (*debug_location, source_location))
576 })
577 .collect()
578 }
579
580 pub fn debug_location_to_address(&self, location: &DebugLocation) -> usize {
582 self.acir_opcode_addresses.debug_location_to_address(location)
583 }
584
585 pub fn address_to_debug_location(&self, address: usize) -> Option<DebugLocation> {
587 self.acir_opcode_addresses.address_to_debug_location(address)
588 }
589
590 pub(super) fn render_opcode_at_location(&self, location: &DebugLocation) -> String {
591 let opcodes = self.get_opcodes_of_circuit(location.circuit_id);
592 match &location.opcode_location {
593 OpcodeLocation::Acir(acir_index) => {
594 let opcode = &opcodes[*acir_index];
595 match opcode {
596 Opcode::BrilligCall { id, .. } => {
597 let first_opcode = &self.unconstrained_functions[id.as_usize()].bytecode[0];
598 format!("BRILLIG {first_opcode:?}")
599 }
600 _ => format!("{opcode:?}"),
601 }
602 }
603 OpcodeLocation::Brillig { acir_index, brillig_index } => match &opcodes[*acir_index] {
604 Opcode::BrilligCall { id, .. } => {
605 let bytecode = &self.unconstrained_functions[id.as_usize()].bytecode;
606 let opcode = &bytecode[*brillig_index];
607 format!(" | {opcode:?}")
608 }
609 _ => String::from(" | invalid"),
610 },
611 }
612 }
613
614 fn step_brillig_opcode(&mut self) -> DebugCommandResult {
615 let Some(mut solver) = self.brillig_solver.take() else {
616 unreachable!("Missing Brillig solver");
617 };
618 match solver.step() {
619 Ok(BrilligSolverStatus::InProgress) => {
620 self.brillig_solver = Some(solver);
621 if self.breakpoint_reached() {
622 DebugCommandResult::BreakpointReached(
623 self.get_current_debug_location()
624 .expect("Breakpoint reached but we have no location"),
625 )
626 } else {
627 DebugCommandResult::Ok
628 }
629 }
630 Ok(BrilligSolverStatus::Finished) => {
631 let status = self.acvm.finish_brillig_with_solver(solver);
632 self.handle_acvm_status(status)
633 }
634 Ok(BrilligSolverStatus::ForeignCallWait(foreign_call)) => {
635 self.brillig_solver = Some(solver);
636 self.handle_foreign_call(foreign_call)
637 }
638 Err(err) => {
639 let error = execution_error_from(
640 err,
641 &self
642 .get_call_stack()
643 .into_iter()
644 .map(|op| op.into())
645 .collect::<Vec<ResolvedOpcodeLocation>>(),
646 );
647 DebugCommandResult::Error(NargoError::ExecutionError(error))
648 }
649 }
650 }
651
652 fn handle_foreign_call(
653 &mut self,
654 foreign_call: ForeignCallWaitInfo<FieldElement>,
655 ) -> DebugCommandResult {
656 let foreign_call_result = self.foreign_call_executor.execute(&foreign_call);
657
658 match foreign_call_result {
659 Ok(foreign_call_result) => {
660 if let Some(mut solver) = self.brillig_solver.take() {
661 solver.resolve_pending_foreign_call(foreign_call_result);
662 self.brillig_solver = Some(solver);
663 } else {
664 self.acvm.resolve_pending_foreign_call(foreign_call_result);
665 }
666 DebugCommandResult::Ok
670 }
671 Err(error) => DebugCommandResult::Error(error.into()),
672 }
673 }
674
675 fn handle_acir_call(
676 &mut self,
677 call_info: AcirCallWaitInfo<FieldElement>,
678 ) -> DebugCommandResult {
679 let callee_circuit = &self.circuits[call_info.id.as_usize()];
680 let callee_witness_map = call_info.initial_witness;
681 let callee_acvm = ACVM::new(
682 self.backend,
683 &callee_circuit.opcodes,
684 callee_witness_map,
685 self.unconstrained_functions,
686 &callee_circuit.assert_messages,
687 );
688 let caller_acvm = std::mem::replace(&mut self.acvm, callee_acvm);
689 self.acvm_stack
690 .push(ExecutionFrame { circuit_id: self.current_circuit_id, acvm: caller_acvm });
691 self.current_circuit_id = call_info.id.0;
692
693 self.handle_acvm_status(self.acvm.get_status().clone())
697 }
698
699 fn handle_acir_call_finished(&mut self) -> DebugCommandResult {
700 let caller_frame = self.acvm_stack.pop().expect("Execution stack should not be empty");
701 let caller_acvm = caller_frame.acvm;
702 let callee_acvm = std::mem::replace(&mut self.acvm, caller_acvm);
703 self.current_circuit_id = caller_frame.circuit_id;
704 let call_solved_witness = callee_acvm.finalize();
705
706 let ACVMStatus::RequiresAcirCall(call_info) = self.acvm.get_status() else {
707 unreachable!("Resolving an ACIR call, the caller is in an invalid state");
708 };
709 let acir_to_call = &self.circuits[call_info.id.as_usize()];
710
711 let mut call_resolved_outputs = Vec::new();
712 for return_witness_index in acir_to_call.return_values.indices() {
713 if let Some(return_value) = call_solved_witness.get_index(return_witness_index) {
714 call_resolved_outputs.push(*return_value);
715 } else {
716 return DebugCommandResult::Error(
717 ExecutionError::SolvingError(
718 OpcodeNotSolvable::MissingAssignment(return_witness_index).into(),
719 None, )
721 .into(),
722 );
723 }
724 }
725 self.acvm.resolve_pending_acir_call(call_resolved_outputs);
726
727 DebugCommandResult::Ok
728 }
729
730 fn handle_acvm_status(&mut self, status: ACVMStatus<FieldElement>) -> DebugCommandResult {
731 match status {
732 ACVMStatus::Solved => {
733 if self.acvm_stack.is_empty() {
734 return DebugCommandResult::Done;
735 }
736 self.handle_acir_call_finished()
737 }
738 ACVMStatus::InProgress => {
739 if self.breakpoint_reached() {
740 DebugCommandResult::BreakpointReached(
741 self.get_current_debug_location()
742 .expect("Breakpoint reached but we have no location"),
743 )
744 } else {
745 DebugCommandResult::Ok
746 }
747 }
748 ACVMStatus::Failure(error) => DebugCommandResult::Error(NargoError::ExecutionError(
749 ExecutionError::SolvingError(error, None),
750 )),
751 ACVMStatus::RequiresForeignCall(foreign_call) => self.handle_foreign_call(foreign_call),
752 ACVMStatus::RequiresAcirCall(call_info) => self.handle_acir_call(call_info),
753 }
754 }
755
756 pub(super) fn step_into_opcode(&mut self) -> DebugCommandResult {
757 if self.brillig_solver.is_some() {
758 return self.step_brillig_opcode();
759 }
760
761 match self.acvm.step_into_brillig() {
762 StepResult::IntoBrillig(solver) => {
763 self.brillig_solver = Some(solver);
764 self.step_brillig_opcode()
765 }
766 StepResult::Status(status) => self.handle_acvm_status(status),
767 }
768 }
769
770 fn get_current_acir_index(&self) -> Option<usize> {
771 self.get_current_debug_location().map(|debug_location| {
772 match debug_location.opcode_location {
773 OpcodeLocation::Acir(acir_index) | OpcodeLocation::Brillig { acir_index, .. } => {
774 acir_index
775 }
776 }
777 })
778 }
779
780 fn step_out_of_brillig_opcode(&mut self) -> DebugCommandResult {
781 let Some(start_acir_index) = self.get_current_acir_index() else {
782 return DebugCommandResult::Done;
783 };
784 loop {
785 let result = self.step_into_opcode();
786 if !matches!(result, DebugCommandResult::Ok) {
787 return result;
788 }
789 let new_acir_index = self.get_current_acir_index().unwrap();
790 if new_acir_index != start_acir_index {
791 return DebugCommandResult::Ok;
792 }
793 }
794 }
795
796 pub(super) fn is_executing_brillig(&self) -> bool {
797 if self.brillig_solver.is_some() {
798 return true;
799 }
800
801 match self.get_current_debug_location() {
802 Some(DebugLocation { opcode_location: OpcodeLocation::Brillig { .. }, .. }) => true,
803 Some(DebugLocation {
804 circuit_id,
805 opcode_location: OpcodeLocation::Acir(acir_index),
806 ..
807 }) => {
808 matches!(
809 self.get_opcodes_of_circuit(circuit_id)[acir_index],
810 Opcode::BrilligCall { .. }
811 )
812 }
813 _ => false,
814 }
815 }
816
817 pub(super) fn step_acir_opcode(&mut self) -> DebugCommandResult {
818 if self.is_executing_brillig() {
819 self.step_out_of_brillig_opcode()
820 } else {
821 let status = self.acvm.solve_opcode();
822 self.handle_acvm_status(status)
823 }
824 }
825
826 pub(super) fn next_into(&mut self) -> DebugCommandResult {
828 let start_location = self.get_current_source_location();
829 loop {
830 let result = self.step_into_opcode();
831 if !matches!(result, DebugCommandResult::Ok) {
832 return result;
833 }
834 let new_location = self.get_current_source_location();
835 if new_location.is_some() && new_location != start_location {
836 return DebugCommandResult::Ok;
837 }
838 }
839 }
840
841 pub(super) fn next_over(&mut self) -> DebugCommandResult {
844 let start_call_stack = self.get_source_call_stack();
845 loop {
846 let result = self.next_into();
847 if !matches!(result, DebugCommandResult::Ok) {
848 return result;
849 }
850 let new_call_stack = self.get_source_call_stack();
851 if new_call_stack.len() <= start_call_stack.len() {
852 return DebugCommandResult::Ok;
853 }
854 }
855 }
856
857 pub(super) fn next_out(&mut self) -> DebugCommandResult {
860 let start_call_stack = self.get_source_call_stack();
861 loop {
862 let result = self.next_into();
863 if !matches!(result, DebugCommandResult::Ok) {
864 return result;
865 }
866 let new_call_stack = self.get_source_call_stack();
867 if new_call_stack.len() < start_call_stack.len() {
868 return DebugCommandResult::Ok;
869 }
870 }
871 }
872
873 pub(super) fn cont(&mut self) -> DebugCommandResult {
874 loop {
875 let result = self.step_into_opcode();
876 if !matches!(result, DebugCommandResult::Ok) {
877 return result;
878 }
879 }
880 }
881
882 pub(super) fn get_brillig_memory(&self) -> Option<&[MemoryValue<FieldElement>]> {
883 self.brillig_solver.as_ref().map(|solver| solver.get_memory())
884 }
885
886 pub(super) fn write_brillig_memory(
887 &mut self,
888 ptr: usize,
889 value: FieldElement,
890 bit_size: BitSize,
891 ) {
892 if let Some(solver) = self.brillig_solver.as_mut() {
893 solver.write_memory_at(
894 ptr,
895 MemoryValue::new_checked(value, bit_size)
896 .expect("Invalid value for the given bit size"),
897 );
898 }
899 }
900
901 pub(super) fn get_variables(&self) -> Vec<StackFrame<FieldElement>> {
902 self.foreign_call_executor.get_variables()
903 }
904
905 pub(super) fn current_stack_frame(&self) -> Option<StackFrame<FieldElement>> {
906 self.foreign_call_executor.current_stack_frame()
907 }
908
909 fn breakpoint_reached(&self) -> bool {
910 if let Some(location) = self.get_current_debug_location() {
911 self.breakpoints.contains(&location)
912 } else {
913 false
914 }
915 }
916
917 pub(super) fn is_valid_debug_location(&self, location: &DebugLocation) -> bool {
918 if location.circuit_id as usize >= self.circuits.len() {
919 return false;
920 }
921 let opcodes = self.get_opcodes_of_circuit(location.circuit_id);
922 match location.opcode_location {
923 OpcodeLocation::Acir(acir_index) => acir_index < opcodes.len(),
924 OpcodeLocation::Brillig { acir_index, brillig_index } => {
925 if acir_index < opcodes.len() {
926 match &opcodes[acir_index] {
927 Opcode::BrilligCall { id, .. } => {
928 let bytecode = &self.unconstrained_functions[id.as_usize()].bytecode;
929 brillig_index < bytecode.len()
930 }
931 _ => false,
932 }
933 } else {
934 false
935 }
936 }
937 }
938 }
939
940 pub(super) fn is_breakpoint_set(&self, location: &DebugLocation) -> bool {
941 self.breakpoints.contains(location)
942 }
943
944 pub(super) fn add_breakpoint(&mut self, location: DebugLocation) -> bool {
945 self.breakpoints.insert(location)
946 }
947
948 pub(super) fn delete_breakpoint(&mut self, location: &DebugLocation) -> bool {
949 self.breakpoints.remove(location)
950 }
951
952 pub(super) fn clear_breakpoints(&mut self) {
953 self.breakpoints.clear();
954 }
955
956 pub(super) fn is_solved(&self) -> bool {
957 matches!(self.acvm.get_status(), ACVMStatus::Solved)
958 }
959
960 pub fn finalize(mut self) -> WitnessStack<FieldElement> {
961 let last_witness_map = self.acvm.finalize();
962 self.witness_stack.push(0, last_witness_map);
963 self.witness_stack
964 }
965
966 pub(super) fn restart(&mut self) {
967 self.current_circuit_id = 0;
970 self.brillig_solver = None;
971 self.witness_stack = WitnessStack::default();
972 self.acvm_stack = vec![];
973 self.foreign_call_executor.restart(self.debug_artifact);
974 self.acvm = initialize_acvm(
975 self.backend,
976 self.circuits,
977 self.initial_witness.clone(),
978 self.unconstrained_functions,
979 );
980 }
981}
982
983fn is_debug_file_in_debug_crate(debug_file: &DebugFile) -> bool {
984 debug_file.path.starts_with("__debug/")
985}
986
987fn build_source_to_opcode_debug_mappings(
990 debug_artifact: &DebugArtifact,
991) -> BTreeMap<FileId, Vec<(usize, DebugLocation)>> {
992 if debug_artifact.debug_symbols.is_empty() {
993 return BTreeMap::new();
994 }
995 let simple_files: BTreeMap<_, _> = debug_artifact
996 .file_map
997 .iter()
998 .filter(|(_, debug_file)| !is_debug_file_in_debug_crate(debug_file))
999 .map(|(file_id, debug_file)| {
1000 (
1001 file_id,
1002 SimpleFile::new(debug_file.path.to_str().unwrap(), debug_file.source.as_str()),
1003 )
1004 })
1005 .collect();
1006
1007 let mut result: BTreeMap<FileId, Vec<(usize, DebugLocation)>> = BTreeMap::new();
1008 for (circuit_id, debug_symbols) in debug_artifact.debug_symbols.iter().enumerate() {
1009 let location_map = debug_symbols
1010 .acir_locations
1011 .iter()
1012 .map(|(key, val)| (OpcodeLocation::Acir(key.index()), *val))
1013 .collect();
1014 add_opcode_locations_map(
1015 debug_symbols,
1016 &location_map,
1017 &mut result,
1018 &simple_files,
1019 circuit_id,
1020 None,
1021 );
1022
1023 for (brillig_function_id, brillig_locations_map) in &debug_symbols.brillig_locations {
1024 let brillig_locations_map = brillig_locations_map
1025 .iter()
1026 .map(|(key, val)| {
1027 (
1028 OpcodeLocation::Brillig { acir_index: 0, brillig_index: key.0 },
1030 *val,
1031 )
1032 })
1033 .collect();
1034
1035 add_opcode_locations_map(
1036 debug_symbols,
1037 &brillig_locations_map,
1038 &mut result,
1039 &simple_files,
1040 circuit_id,
1041 Some(*brillig_function_id),
1042 );
1043 }
1044 }
1045 result.iter_mut().for_each(|(_, file_locations)| file_locations.sort_by_key(|x| (x.0, x.1)));
1046
1047 result
1048}
1049
1050fn add_opcode_locations_map(
1051 debug_info: &DebugInfo,
1052 opcode_to_locations: &BTreeMap<OpcodeLocation, CallStackId>,
1053 source_to_locations: &mut BTreeMap<FileId, Vec<(usize, DebugLocation)>>,
1054 simple_files: &BTreeMap<&FileId, SimpleFile<&str, &str>>,
1055 circuit_id: usize,
1056 brillig_function_id: Option<BrilligFunctionId>,
1057) {
1058 for (opcode_location, source_locations) in opcode_to_locations {
1059 let source_locations = debug_info.location_tree.get_call_stack(*source_locations);
1060 source_locations.iter().for_each(|source_location| {
1061 let span = source_location.span;
1062 let file_id = source_location.file;
1063 let Some(file) = simple_files.get(&file_id) else {
1064 return;
1065 };
1066 let Ok(line_index) = file.line_index((), span.start() as usize) else {
1067 return;
1068 };
1069 let line_number = line_index + 1;
1070
1071 let debug_location = DebugLocation {
1072 circuit_id: circuit_id as u32,
1073 opcode_location: *opcode_location,
1074 brillig_function_id,
1075 };
1076 source_to_locations.entry(file_id).or_default().push((line_number, debug_location));
1077 });
1078 }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use super::*;
1084
1085 use crate::foreign_calls::DefaultDebugForeignCallExecutor;
1086 use acvm::{
1087 acir::{
1088 AcirField,
1089 brillig::{HeapVector, IntegerBitSize},
1090 circuit::{
1091 brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs},
1092 opcodes::{AcirFunctionId, BlockId, BlockType},
1093 },
1094 native_types::Expression,
1095 },
1096 blackbox_solver::StubbedBlackBoxSolver,
1097 brillig_vm::brillig::{
1098 BinaryFieldOp, HeapValueType, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray,
1099 },
1100 };
1101
1102 #[test]
1103 fn test_resolve_foreign_calls_stepping_into_brillig() {
1104 let solver = StubbedBlackBoxSolver::default();
1105 let fe_1 = FieldElement::one();
1106 let w_x = Witness(1);
1107
1108 let brillig_bytecode = BrilligBytecode {
1109 bytecode: vec![
1110 BrilligOpcode::Const {
1111 destination: MemoryAddress::direct(1),
1112 bit_size: BitSize::Integer(IntegerBitSize::U32),
1113 value: FieldElement::from(1u64),
1114 },
1115 BrilligOpcode::Const {
1116 destination: MemoryAddress::direct(2),
1117 bit_size: BitSize::Integer(IntegerBitSize::U32),
1118 value: FieldElement::from(0u64),
1119 },
1120 BrilligOpcode::CalldataCopy {
1121 destination_address: MemoryAddress::direct(0),
1122 size_address: MemoryAddress::direct(1),
1123 offset_address: MemoryAddress::direct(2),
1124 },
1125 BrilligOpcode::ForeignCall {
1126 function: "clear_mock".into(),
1127 destinations: vec![],
1128 destination_value_types: vec![],
1129 inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::direct(0))],
1130 input_value_types: vec![HeapValueType::field()],
1131 },
1132 BrilligOpcode::Stop {
1133 return_data: HeapVector {
1134 pointer: MemoryAddress::direct(2),
1135 size: MemoryAddress::direct(2),
1136 },
1137 },
1138 ],
1139 };
1140 let opcodes = vec![Opcode::BrilligCall {
1141 id: BrilligFunctionId(0),
1142 inputs: vec![BrilligInputs::Single(Expression {
1143 linear_combinations: vec![(fe_1, w_x)],
1144 ..Expression::default()
1145 })],
1146 outputs: vec![],
1147 predicate: None,
1148 }];
1149 let brillig_functions = &[brillig_bytecode];
1150 let current_witness_index = 2;
1151 let circuit = Circuit { current_witness_index, opcodes, ..Circuit::default() };
1152 let circuits = &[circuit];
1153
1154 let debug_symbols = vec![];
1155 let file_map = BTreeMap::new();
1156 let debug_artifact = &DebugArtifact { debug_symbols, file_map };
1157
1158 let initial_witness = BTreeMap::from([(Witness(1), fe_1)]).into();
1159
1160 let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact(
1161 std::io::stdout(),
1162 None,
1163 debug_artifact,
1164 None,
1165 String::new(),
1166 ));
1167 let mut context = DebugContext::<StubbedBlackBoxSolver>::new(
1168 &solver,
1169 circuits,
1170 debug_artifact,
1171 initial_witness,
1172 foreign_call_executor,
1173 brillig_functions,
1174 );
1175
1176 assert_eq!(
1177 context.get_current_debug_location(),
1178 Some(DebugLocation {
1179 circuit_id: 0,
1180 opcode_location: OpcodeLocation::Acir(0),
1181 brillig_function_id: None,
1182 })
1183 );
1184
1185 let result = context.step_into_opcode();
1187 assert!(matches!(result, DebugCommandResult::Ok));
1188 assert_eq!(
1189 context.get_current_debug_location(),
1190 Some(DebugLocation {
1191 circuit_id: 0,
1192 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 },
1193 brillig_function_id: Some(BrilligFunctionId(0)),
1194 })
1195 );
1196
1197 let result = context.step_into_opcode();
1199 assert!(matches!(result, DebugCommandResult::Ok));
1200 assert_eq!(
1201 context.get_current_debug_location(),
1202 Some(DebugLocation {
1203 circuit_id: 0,
1204 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 },
1205 brillig_function_id: Some(BrilligFunctionId(0)),
1206 })
1207 );
1208
1209 let result = context.step_into_opcode();
1212 assert!(matches!(result, DebugCommandResult::Ok));
1213 assert_eq!(
1214 context.get_current_debug_location(),
1215 Some(DebugLocation {
1216 circuit_id: 0,
1217 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 },
1218 brillig_function_id: Some(BrilligFunctionId(0)),
1219 })
1220 );
1221
1222 let result = context.step_into_opcode();
1224 assert!(matches!(result, DebugCommandResult::Ok));
1225 assert_eq!(
1226 context.get_current_debug_location(),
1227 Some(DebugLocation {
1228 circuit_id: 0,
1229 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 },
1230 brillig_function_id: Some(BrilligFunctionId(0)),
1231 })
1232 );
1233
1234 let result = context.step_into_opcode();
1236 assert!(matches!(result, DebugCommandResult::Ok));
1237 assert_eq!(
1238 context.get_current_debug_location(),
1239 Some(DebugLocation {
1240 circuit_id: 0,
1241 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 4 },
1242 brillig_function_id: Some(BrilligFunctionId(0)),
1243 })
1244 );
1245
1246 let result = context.step_into_opcode();
1248 assert!(matches!(result, DebugCommandResult::Done));
1249 assert_eq!(context.get_current_debug_location(), None);
1250 }
1251
1252 #[test]
1253 fn test_break_brillig_block_while_stepping_acir_opcodes() {
1254 let solver = StubbedBlackBoxSolver::default();
1255 let fe_0 = FieldElement::zero();
1256 let fe_1 = FieldElement::one();
1257 let w_x = Witness(1);
1258 let w_y = Witness(2);
1259 let w_z = Witness(3);
1260
1261 let zero_usize = MemoryAddress::direct(2);
1262 let one_usize = MemoryAddress::direct(3);
1263
1264 let brillig_bytecode = BrilligBytecode {
1266 bytecode: vec![
1267 BrilligOpcode::Const {
1268 destination: MemoryAddress::direct(0),
1269 bit_size: BitSize::Integer(IntegerBitSize::U32),
1270 value: FieldElement::from(2u64),
1271 },
1272 BrilligOpcode::Const {
1273 destination: zero_usize,
1274 bit_size: BitSize::Integer(IntegerBitSize::U32),
1275 value: FieldElement::from(0u64),
1276 },
1277 BrilligOpcode::Const {
1278 destination: one_usize,
1279 bit_size: BitSize::Integer(IntegerBitSize::U32),
1280 value: FieldElement::from(1u64),
1281 },
1282 BrilligOpcode::CalldataCopy {
1283 destination_address: MemoryAddress::direct(0),
1284 size_address: MemoryAddress::direct(0),
1285 offset_address: zero_usize,
1286 },
1287 BrilligOpcode::BinaryFieldOp {
1288 destination: MemoryAddress::direct(0),
1289 op: BinaryFieldOp::Add,
1290 lhs: MemoryAddress::direct(0),
1291 rhs: MemoryAddress::direct(1),
1292 },
1293 BrilligOpcode::Stop {
1294 return_data: HeapVector { pointer: zero_usize, size: one_usize },
1295 },
1296 ],
1297 };
1298 let opcodes = vec![
1299 Opcode::BrilligCall {
1301 id: BrilligFunctionId(0),
1302 inputs: vec![
1303 BrilligInputs::Single(Expression {
1304 linear_combinations: vec![(fe_1, w_x)],
1305 ..Expression::default()
1306 }),
1307 BrilligInputs::Single(Expression {
1308 linear_combinations: vec![(fe_1, w_y)],
1309 ..Expression::default()
1310 }),
1311 ],
1312 outputs: vec![BrilligOutputs::Simple(w_z)],
1313 predicate: None,
1314 },
1315 Opcode::AssertZero(Expression {
1317 mul_terms: vec![],
1318 linear_combinations: vec![(fe_1, w_x), (fe_1, w_y), (-fe_1, w_z)],
1319 q_c: fe_0,
1320 }),
1321 ];
1322 let current_witness_index = 3;
1323 let circuit = Circuit { current_witness_index, opcodes, ..Circuit::default() };
1324 let circuits = &[circuit];
1325
1326 let debug_symbols = vec![];
1327 let file_map = BTreeMap::new();
1328 let debug_artifact = &DebugArtifact { debug_symbols, file_map };
1329
1330 let initial_witness = BTreeMap::from([(Witness(1), fe_1), (Witness(2), fe_1)]).into();
1331
1332 let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact(
1333 std::io::stdout(),
1334 None,
1335 debug_artifact,
1336 None,
1337 String::new(),
1338 ));
1339 let brillig_functions = &[brillig_bytecode];
1340 let mut context = DebugContext::<StubbedBlackBoxSolver>::new(
1341 &solver,
1342 circuits,
1343 debug_artifact,
1344 initial_witness,
1345 foreign_call_executor,
1346 brillig_functions,
1347 );
1348
1349 let breakpoint_location = DebugLocation {
1351 circuit_id: 0,
1352 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 },
1353 brillig_function_id: Some(BrilligFunctionId(0)),
1354 };
1355 assert!(context.add_breakpoint(breakpoint_location));
1356
1357 let result = context.step_acir_opcode();
1359 assert!(matches!(result, DebugCommandResult::BreakpointReached(_)));
1360 assert_eq!(context.get_current_debug_location(), Some(breakpoint_location));
1361
1362 let result = context.step_acir_opcode();
1364 assert!(matches!(result, DebugCommandResult::Ok));
1365 assert_eq!(
1366 context.get_current_debug_location(),
1367 Some(DebugLocation {
1368 circuit_id: 0,
1369 opcode_location: OpcodeLocation::Acir(1),
1370 brillig_function_id: None
1371 })
1372 );
1373
1374 let result = context.step_acir_opcode();
1376 assert!(matches!(result, DebugCommandResult::Done));
1377 assert_eq!(context.get_current_debug_location(), None);
1378 }
1379
1380 #[test]
1381 fn test_address_debug_location_mapping() {
1382 let solver = StubbedBlackBoxSolver::default();
1383 let brillig_one =
1384 BrilligBytecode { bytecode: vec![BrilligOpcode::Return, BrilligOpcode::Return] };
1385 let brillig_two = BrilligBytecode {
1386 bytecode: vec![BrilligOpcode::Return, BrilligOpcode::Return, BrilligOpcode::Return],
1387 };
1388
1389 let circuit_one = Circuit {
1390 opcodes: vec![
1391 Opcode::MemoryInit {
1392 block_id: BlockId(0),
1393 init: vec![],
1394 block_type: BlockType::Memory,
1395 },
1396 Opcode::BrilligCall {
1397 id: BrilligFunctionId(0),
1398 inputs: vec![],
1399 outputs: vec![],
1400 predicate: None,
1401 },
1402 Opcode::Call {
1403 id: AcirFunctionId(1),
1404 inputs: vec![],
1405 outputs: vec![],
1406 predicate: None,
1407 },
1408 Opcode::AssertZero(Expression::default()),
1409 ],
1410 ..Circuit::default()
1411 };
1412 let circuit_two = Circuit {
1413 opcodes: vec![
1414 Opcode::BrilligCall {
1415 id: BrilligFunctionId(1),
1416 inputs: vec![],
1417 outputs: vec![],
1418 predicate: None,
1419 },
1420 Opcode::AssertZero(Expression::default()),
1421 ],
1422 ..Circuit::default()
1423 };
1424 let circuits = vec![circuit_one, circuit_two];
1425 let debug_artifact = DebugArtifact { debug_symbols: vec![], file_map: BTreeMap::new() };
1426 let brillig_functions = &[brillig_one, brillig_two];
1427
1428 let context = DebugContext::<StubbedBlackBoxSolver>::new(
1429 &solver,
1430 &circuits,
1431 &debug_artifact,
1432 WitnessMap::new(),
1433 Box::new(DefaultDebugForeignCallExecutor::new(
1434 std::io::stdout(),
1435 None,
1436 None,
1437 String::new(),
1438 )),
1439 brillig_functions,
1440 );
1441
1442 let locations =
1443 (0..=8).map(|address| context.address_to_debug_location(address)).collect::<Vec<_>>();
1444
1445 assert_eq!(
1447 locations,
1448 vec![
1449 Some(DebugLocation {
1450 circuit_id: 0,
1451 opcode_location: OpcodeLocation::Acir(0),
1452 brillig_function_id: None
1453 }),
1454 Some(DebugLocation {
1455 circuit_id: 0,
1456 opcode_location: OpcodeLocation::Acir(1),
1457 brillig_function_id: None
1458 }),
1459 Some(DebugLocation {
1460 circuit_id: 0,
1461 opcode_location: OpcodeLocation::Brillig { acir_index: 1, brillig_index: 1 },
1462 brillig_function_id: Some(BrilligFunctionId(0)),
1463 }),
1464 Some(DebugLocation {
1465 circuit_id: 0,
1466 opcode_location: OpcodeLocation::Acir(2),
1467 brillig_function_id: None
1468 }),
1469 Some(DebugLocation {
1470 circuit_id: 0,
1471 opcode_location: OpcodeLocation::Acir(3),
1472 brillig_function_id: None
1473 }),
1474 Some(DebugLocation {
1475 circuit_id: 1,
1476 opcode_location: OpcodeLocation::Acir(0),
1477 brillig_function_id: None
1478 }),
1479 Some(DebugLocation {
1480 circuit_id: 1,
1481 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 },
1482 brillig_function_id: Some(BrilligFunctionId(1)),
1483 }),
1484 Some(DebugLocation {
1485 circuit_id: 1,
1486 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 },
1487 brillig_function_id: Some(BrilligFunctionId(1)),
1488 }),
1489 Some(DebugLocation {
1490 circuit_id: 1,
1491 opcode_location: OpcodeLocation::Acir(1),
1492 brillig_function_id: None
1493 }),
1494 ]
1495 );
1496
1497 let addresses = locations
1498 .iter()
1499 .flatten()
1500 .map(|location| context.debug_location_to_address(location))
1501 .collect::<Vec<_>>();
1502
1503 assert_eq!(addresses, (0..=8).collect::<Vec<_>>());
1505
1506 assert_eq!(None, context.address_to_debug_location(9));
1508 assert_eq!(
1509 1,
1510 context.debug_location_to_address(&DebugLocation {
1511 circuit_id: 0,
1512 opcode_location: OpcodeLocation::Brillig { acir_index: 1, brillig_index: 0 },
1513 brillig_function_id: Some(BrilligFunctionId(0)),
1514 })
1515 );
1516 assert_eq!(
1517 5,
1518 context.debug_location_to_address(&DebugLocation {
1519 circuit_id: 1,
1520 opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 0 },
1521 brillig_function_id: Some(BrilligFunctionId(1)),
1522 })
1523 );
1524 }
1525}