1use std::{
2 cell::{Cell, RefCell},
3 collections::{BTreeMap, VecDeque},
4 fmt,
5 ops::Deref,
6 rc::Rc,
7 sync::Arc,
8};
9
10use miden_assembly_syntax::{Library, diagnostics::Report};
11use miden_core::{
12 Word,
13 program::{Program, StackInputs},
14};
15use miden_debug_types::{SourceManager, SourceManagerExt};
16use miden_mast_package::Dependency;
17use miden_processor::{
18 ContextId, ExecutionError, ExecutionOptions, FastProcessor, Felt,
19 advice::{AdviceInputs, AdviceMutation},
20 event::{EventHandler, EventName},
21 mast::MastForest,
22 trace::RowIndex,
23};
24
25use super::{DebugExecutor, DebuggerHost, ExecutionConfig, ExecutionTrace, TraceEvent};
26use crate::{debug::CallStack, felt::FromMidenRepr};
27
28pub struct Executor {
35 stack: StackInputs,
36 advice: AdviceInputs,
37 options: ExecutionOptions,
38 libraries: Vec<Arc<Library>>,
39 event_handlers: Vec<(EventName, Arc<dyn EventHandler>)>,
40 dependency_resolver: BTreeMap<Word, Arc<Library>>,
41}
42impl Executor {
43 pub fn new(args: Vec<Felt>) -> Self {
45 let config = ExecutionConfig {
46 inputs: StackInputs::new(&args).expect("invalid stack inputs"),
47 ..Default::default()
48 };
49
50 Self::from_config(config)
51 }
52
53 pub fn from_config(config: ExecutionConfig) -> Self {
57 let ExecutionConfig {
58 inputs,
59 advice_inputs,
60 options,
61 } = config;
62 let options = options.with_tracing(true).with_debugging(true);
63 let dependency_resolver = BTreeMap::new();
64
65 Self {
66 stack: inputs,
67 advice: advice_inputs,
68 options,
69 libraries: Default::default(),
70 event_handlers: Default::default(),
71 dependency_resolver,
72 }
73 }
74
75 pub fn for_package<I>(package: &miden_mast_package::Package, args: I) -> Result<Self, Report>
77 where
78 I: IntoIterator<Item = Felt>,
79 {
80 use miden_assembly_syntax::DisplayHex;
81 log::debug!(
82 "creating executor for package '{}' (digest={})",
83 package.name,
84 DisplayHex::new(&package.digest().as_bytes())
85 );
86 let mut exec = Self::new(args.into_iter().collect());
87 let dependencies = package.manifest.dependencies();
88 exec.with_dependencies(dependencies)?;
89 log::debug!("executor created");
90 Ok(exec)
91 }
92
93 pub fn with_dependencies<'a>(
95 &mut self,
96 dependencies: impl Iterator<Item = &'a Dependency>,
97 ) -> Result<&mut Self, Report> {
98 for dep in dependencies {
99 let digest = dep.digest;
100 match self.dependency_resolver.get(&digest) {
101 Some(lib) => {
102 log::debug!("dependency {dep:?} resolved");
103 self.with_library(lib.clone());
104 }
105 None => panic!("{dep:?} not found in resolver"),
106 }
107 }
108
109 log::debug!("executor created");
110
111 Ok(self)
112 }
113
114 pub fn with_advice_inputs(&mut self, advice: AdviceInputs) -> &mut Self {
116 self.advice.extend(advice);
117 self
118 }
119
120 pub fn with_library(&mut self, lib: Arc<Library>) -> &mut Self {
122 self.libraries.push(lib);
123 self
124 }
125
126 pub fn register_event_handler(
128 &mut self,
129 event: EventName,
130 handler: Arc<dyn EventHandler>,
131 ) -> Result<&mut Self, ExecutionError> {
132 self.event_handlers.push((event, handler));
133 Ok(self)
134 }
135
136 pub fn into_debug(
139 mut self,
140 program: &Program,
141 source_manager: Arc<dyn SourceManager>,
142 ) -> DebugExecutor {
143 log::debug!("creating debug executor");
144
145 let mut host = DebuggerHost::new(source_manager.clone());
146 for lib in core::mem::take(&mut self.libraries) {
147 host.load_mast_forest(lib.mast_forest().clone());
148 }
149 for (event, handler) in core::mem::take(&mut self.event_handlers) {
150 host.register_event_handler(event, handler)
151 .expect("failed to register debug executor event handler");
152 }
153
154 let trace_events: Rc<RefCell<BTreeMap<RowIndex, TraceEvent>>> = Rc::new(Default::default());
155 let frame_start_events = Rc::clone(&trace_events);
156 host.register_trace_handler(TraceEvent::FrameStart, move |clk, event| {
157 frame_start_events.borrow_mut().insert(clk, event);
158 });
159 let frame_end_events = Rc::clone(&trace_events);
160 host.register_trace_handler(TraceEvent::FrameEnd, move |clk, event| {
161 frame_end_events.borrow_mut().insert(clk, event);
162 });
163 let assertion_events = Rc::clone(&trace_events);
164 host.register_assert_failed_tracer(move |clk, event| {
165 assertion_events.borrow_mut().insert(clk, event);
166 });
167
168 let mut processor = FastProcessor::new(self.stack)
169 .with_advice(self.advice)
170 .with_options(self.options)
171 .with_debugging(true)
172 .with_tracing(true);
173
174 let root_context = ContextId::root();
175 let resume_ctx = processor
176 .get_initial_resume_context(program)
177 .expect("failed to get initial resume context");
178
179 let callstack = CallStack::new(trace_events);
180 DebugExecutor {
181 processor,
182 host,
183 resume_ctx: Some(resume_ctx),
184 current_stack: vec![],
185 current_op: None,
186 current_asmop: None,
187 stack_outputs: Default::default(),
188 contexts: Default::default(),
189 root_context,
190 current_context: root_context,
191 callstack,
192 recent: VecDeque::with_capacity(5),
193 cycle: 0,
194 stopped: false,
195 }
196 }
197
198 pub fn into_debug_with_replay(
207 mut self,
208 program: &Program,
209 source_manager: Arc<dyn SourceManager>,
210 extra_forests: Vec<Arc<MastForest>>,
211 event_replay: VecDeque<Vec<AdviceMutation>>,
212 ) -> DebugExecutor {
213 log::debug!("creating debug executor with event replay");
214
215 let mut host = DebuggerHost::new(source_manager.clone());
216 for lib in core::mem::take(&mut self.libraries) {
217 host.load_mast_forest(lib.mast_forest().clone());
218 }
219 for forest in extra_forests {
220 host.load_mast_forest(forest);
221 }
222 host.set_event_replay(event_replay);
223
224 let trace_events: Rc<RefCell<BTreeMap<RowIndex, TraceEvent>>> = Rc::new(Default::default());
225 let frame_start_events = Rc::clone(&trace_events);
226 host.register_trace_handler(TraceEvent::FrameStart, move |clk, event| {
227 frame_start_events.borrow_mut().insert(clk, event);
228 });
229 let frame_end_events = Rc::clone(&trace_events);
230 host.register_trace_handler(TraceEvent::FrameEnd, move |clk, event| {
231 frame_end_events.borrow_mut().insert(clk, event);
232 });
233 let assertion_events = Rc::clone(&trace_events);
234 host.register_assert_failed_tracer(move |clk, event| {
235 assertion_events.borrow_mut().insert(clk, event);
236 });
237
238 let mut processor = FastProcessor::new(self.stack)
239 .with_advice(self.advice)
240 .with_options(self.options)
241 .with_debugging(true)
242 .with_tracing(true);
243
244 let root_context = ContextId::root();
245 let resume_ctx = processor
246 .get_initial_resume_context(program)
247 .expect("failed to get initial resume context");
248
249 let callstack = CallStack::new(trace_events);
250 DebugExecutor {
251 processor,
252 host,
253 resume_ctx: Some(resume_ctx),
254 current_stack: vec![],
255 current_op: None,
256 current_asmop: None,
257 stack_outputs: Default::default(),
258 contexts: Default::default(),
259 root_context,
260 current_context: root_context,
261 callstack,
262 recent: VecDeque::with_capacity(5),
263 cycle: 0,
264 stopped: false,
265 }
266 }
267
268 pub fn capture_trace(
270 self,
271 program: &Program,
272 source_manager: Arc<dyn SourceManager>,
273 ) -> ExecutionTrace {
274 let mut executor = self.into_debug(program, source_manager);
275 loop {
276 if executor.stopped {
277 break;
278 }
279 match executor.step() {
280 Ok(_) => continue,
281 Err(_) => break,
282 }
283 }
284 executor.into_execution_trace()
285 }
286
287 #[track_caller]
289 pub fn execute(
290 self,
291 program: &Program,
292 source_manager: Arc<dyn SourceManager>,
293 ) -> ExecutionTrace {
294 let mut executor = self.into_debug(program, source_manager.clone());
295 loop {
296 if executor.stopped {
297 break;
298 }
299 match executor.step() {
300 Ok(_) => {
301 if log::log_enabled!(target: "executor", log::Level::Trace)
302 && let (Some(op), Some(asmop)) =
303 (executor.current_op, executor.current_asmop.as_ref())
304 {
305 dbg!(&executor.current_stack);
306 let source_loc = asmop.location().map(|loc| {
307 let path = std::path::Path::new(loc.uri().path());
308 let file = source_manager.load_file(path).unwrap();
309 (file, loc.start)
310 });
311 if let Some((source_file, line_start)) = source_loc {
312 let line_number = source_file.content().line_index(line_start).number();
313 log::trace!(target: "executor", "in {} (located at {}:{})", asmop.context_name(), source_file.deref().uri().as_str(), &line_number);
314 } else {
315 log::trace!(target: "executor", "in {} (no source location available)", asmop.context_name());
316 }
317 log::trace!(target: "executor", " executed `{op:?}` of `{}` ({} cycles)", asmop.op(), asmop.num_cycles());
318 log::trace!(target: "executor", " stack state: {:#?}", &executor.current_stack);
319 }
320 }
321 Err(err) => {
322 render_execution_error(err, &executor, &source_manager);
323 }
324 }
325 }
326
327 executor.into_execution_trace()
328 }
329
330 pub fn execute_into<T>(self, program: &Program, source_manager: Arc<dyn SourceManager>) -> T
332 where
333 T: FromMidenRepr + PartialEq,
334 {
335 let out = self.execute(program, source_manager);
336 out.parse_result().expect("invalid result")
337 }
338
339 pub fn dependency_resolver_mut(&mut self) -> &mut BTreeMap<Word, Arc<Library>> {
340 &mut self.dependency_resolver
341 }
342
343 pub fn register_library_dependency(&mut self, lib: Arc<Library>) {
345 let digest = *lib.digest();
346 self.dependency_resolver.insert(digest, lib);
347 }
348}
349
350#[track_caller]
351fn render_execution_error(
352 err: ExecutionError,
353 execution_state: &DebugExecutor,
354 source_manager: &dyn SourceManager,
355) -> ! {
356 use miden_assembly_syntax::diagnostics::{
357 LabeledSpan, miette::miette, reporting::PrintDiagnostic,
358 };
359
360 let stacktrace = execution_state.callstack.stacktrace(&execution_state.recent, source_manager);
361
362 eprintln!("{stacktrace}");
363
364 if !execution_state.current_stack.is_empty() {
365 let stack = execution_state.current_stack.iter().map(|elem| elem.as_canonical_u64());
366 let stack = DisplayValues::new(stack);
367 eprintln!(
368 "\nLast Known State (at most recent instruction which succeeded):
369 | Operand Stack: [{stack}]
370 "
371 );
372
373 let mut labels = vec![];
374 if let Some(span) = stacktrace
375 .current_frame()
376 .and_then(|frame| frame.location.as_ref())
377 .map(|loc| loc.span)
378 {
379 labels.push(LabeledSpan::new_with_span(
380 None,
381 span.start().to_usize()..span.end().to_usize(),
382 ));
383 }
384 let report = miette!(
385 labels = labels,
386 "program execution failed at step {step} (cycle {cycle}): {err}",
387 step = execution_state.cycle,
388 cycle = execution_state.cycle,
389 );
390 let report = match stacktrace
391 .current_frame()
392 .and_then(|frame| frame.location.as_ref())
393 .map(|loc| loc.source_file.clone())
394 {
395 Some(source) => report.with_source_code(source),
396 None => report,
397 };
398
399 panic!("{}", PrintDiagnostic::new(report));
400 } else {
401 panic!("program execution failed at step {step}: {err}", step = execution_state.cycle);
402 }
403}
404
405struct DisplayValues<T>(Cell<Option<T>>);
407
408impl<T> DisplayValues<T> {
409 pub fn new(inner: T) -> Self {
410 Self(Cell::new(Some(inner)))
411 }
412}
413
414impl<T, I> fmt::Display for DisplayValues<I>
415where
416 T: fmt::Display,
417 I: Iterator<Item = T>,
418{
419 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
420 let iter = self.0.take().unwrap();
421 for (i, item) in iter.enumerate() {
422 if i == 0 {
423 write!(f, "{item}")?;
424 } else {
425 write!(f, ", {item}")?;
426 }
427 }
428 Ok(())
429 }
430}