use alloc::{sync::Arc, vec::Vec};
use core::ops::ControlFlow;
use miden_core::{mast::MastForest, program::KernelDescriptor};
use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
use crate::{
ExecutionError, FastProcessor, SourceInlineCallContext, Stopper,
continuation_stack::{Continuation, ContinuationStack},
};
#[derive(Debug)]
pub struct ResumeContext {
pub(crate) current_forest: Arc<MastForest>,
pub(crate) continuation_stack: ContinuationStack<Arc<MastForest>>,
pub(crate) kernel: KernelDescriptor,
pub(crate) package_debug_info: Option<Arc<PackageDebugInfo>>,
pub(crate) inline_call_contexts: Vec<Option<SourceInlineCallContext>>,
}
impl ResumeContext {
pub fn continuation_stack(&self) -> &ContinuationStack<Arc<MastForest>> {
&self.continuation_stack
}
pub fn current_forest(&self) -> &Arc<MastForest> {
&self.current_forest
}
pub fn debug_info(&self) -> Option<Arc<PackageDebugInfo>> {
self.package_debug_info.clone()
}
pub fn next_source_node_id(&self) -> Option<DebugSourceNodeId> {
self.continuation_stack
.peek_continuation_with_source_node_id()
.and_then(|(_, source_node_id)| source_node_id)
}
pub fn inherited_inline_call_contexts(&self) -> impl Iterator<Item = &SourceInlineCallContext> {
let effective_depth = self.continuation_stack.iter_continuations_for_next_clock().fold(
self.inline_call_contexts.len(),
|depth, continuation| match continuation {
Continuation::EnterForest { inline_context_depth, .. } => *inline_context_depth,
_ => depth,
},
);
self.inline_call_contexts[..effective_depth.min(self.inline_call_contexts.len())]
.iter()
.rev()
.filter_map(Option::as_ref)
}
pub fn kernel(&self) -> &KernelDescriptor {
&self.kernel
}
}
pub struct NeverStopper;
impl Stopper for NeverStopper {
type Processor = FastProcessor;
type Forest = Arc<MastForest>;
#[inline(always)]
fn should_stop(
&self,
processor: &FastProcessor,
continuation_stack: &ContinuationStack<Arc<MastForest>>,
_continuation_after_stop: impl FnOnce() -> Option<(
Continuation<Arc<MastForest>>,
Option<DebugSourceNodeId>,
)>,
) -> ControlFlow<BreakReason<Arc<MastForest>>> {
check_if_max_cycles_exceeded(processor)?;
check_if_continuation_stack_too_large(processor, continuation_stack)
}
}
pub struct StepStopper;
impl Stopper for StepStopper {
type Processor = FastProcessor;
type Forest = Arc<MastForest>;
#[inline(always)]
fn should_stop(
&self,
processor: &FastProcessor,
continuation_stack: &ContinuationStack<Arc<MastForest>>,
continuation_after_stop: impl FnOnce() -> Option<(
Continuation<Arc<MastForest>>,
Option<DebugSourceNodeId>,
)>,
) -> ControlFlow<BreakReason<Arc<MastForest>>> {
check_if_max_cycles_exceeded(processor)?;
check_if_continuation_stack_too_large(processor, continuation_stack)?;
ControlFlow::Break(BreakReason::Stopped(continuation_after_stop()))
}
}
#[inline(always)]
fn check_if_max_cycles_exceeded<F>(processor: &FastProcessor) -> ControlFlow<BreakReason<F>> {
if processor.clk > processor.options.max_cycles() as usize {
ControlFlow::Break(BreakReason::Err(ExecutionError::CycleLimitExceeded(
processor.options.max_cycles(),
)))
} else {
ControlFlow::Continue(())
}
}
#[inline(always)]
fn check_if_continuation_stack_too_large<F>(
processor: &FastProcessor,
continuation_stack: &ContinuationStack<F>,
) -> ControlFlow<BreakReason<F>> {
if continuation_stack.len() > processor.options.max_num_continuations() {
ControlFlow::Break(BreakReason::Err(ExecutionError::Internal(
"continuation stack size exceeded the allowed maximum",
)))
} else {
ControlFlow::Continue(())
}
}
#[derive(Debug)]
pub enum BreakReason<F> {
Err(ExecutionError),
Stopped(Option<(Continuation<F>, Option<DebugSourceNodeId>)>),
}