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