Skip to main content

sim_lib_machine/
frame.rs

1use sim_lib_control::{AdmissionLimit, WorkLimit};
2
3use crate::ManagedRootSource;
4use crate::{CodeCursor, SlotFile, UnitStack, ValueWidthPolicy};
5
6/// One admitted activation, composed entirely from bounded machine state.
7pub struct Frame<P, K, R, H>
8where
9    P: ValueWidthPolicy,
10{
11    slots: SlotFile<P>,
12    operands: UnitStack<P>,
13    cursor: CodeCursor,
14    continuation: Option<K>,
15    roots: R,
16    handlers: H,
17}
18
19impl<P, K, R, H> Frame<P, K, R, H>
20where
21    P: ValueWidthPolicy,
22{
23    /// Composes a frame at a validated code cursor with independently bounded storage.
24    pub fn new(
25        slot_limit: AdmissionLimit,
26        operand_limit: WorkLimit,
27        cursor: CodeCursor,
28        continuation: Option<K>,
29        roots: R,
30        handlers: H,
31    ) -> Self {
32        Self {
33            slots: SlotFile::new(slot_limit),
34            operands: UnitStack::new(operand_limit),
35            cursor,
36            continuation,
37            roots,
38            handlers,
39        }
40    }
41
42    /// Returns the bounded local-slot file.
43    pub fn slots(&self) -> &SlotFile<P> {
44        &self.slots
45    }
46
47    /// Returns the mutable bounded local-slot file.
48    pub fn slots_mut(&mut self) -> &mut SlotFile<P> {
49        &mut self.slots
50    }
51
52    /// Returns the bounded operand stack.
53    pub fn operands(&self) -> &UnitStack<P> {
54        &self.operands
55    }
56
57    /// Returns the mutable bounded operand stack.
58    pub fn operands_mut(&mut self) -> &mut UnitStack<P> {
59        &mut self.operands
60    }
61
62    /// Returns the current validated instruction cursor.
63    pub fn cursor(&self) -> CodeCursor {
64        self.cursor
65    }
66
67    /// Moves the frame to another validated instruction cursor.
68    pub fn set_cursor(&mut self, cursor: CodeCursor) {
69        self.cursor = cursor;
70    }
71
72    /// Returns the caller-defined continuation state.
73    pub fn continuation(&self) -> Option<&K> {
74        self.continuation.as_ref()
75    }
76
77    /// Returns the caller-defined managed-root state.
78    pub fn roots(&self) -> &R {
79        &self.roots
80    }
81
82    /// Returns the mutable caller-defined managed-root state.
83    pub fn roots_mut(&mut self) -> &mut R {
84        &mut self.roots
85    }
86
87    /// Returns the caller-defined handler state.
88    pub fn handlers(&self) -> &H {
89        &self.handlers
90    }
91
92    /// Returns the mutable caller-defined handler state.
93    pub fn handlers_mut(&mut self) -> &mut H {
94        &mut self.handlers
95    }
96}
97
98impl<P, K, R, H> ManagedRootSource for Frame<P, K, R, H>
99where
100    P: ValueWidthPolicy,
101    P::Value: ManagedRootSource,
102    K: ManagedRootSource,
103    R: ManagedRootSource,
104    H: ManagedRootSource,
105{
106    fn visit_managed_roots(
107        &self,
108        visit: &mut dyn FnMut(sim_lib_mutation::ManagedId) -> bool,
109    ) -> bool {
110        let mut complete = true;
111        self.slots
112            .visit_values(|value| complete = complete && value.visit_managed_roots(visit));
113        if !complete {
114            return false;
115        }
116        self.operands
117            .visit_values(|value| complete = complete && value.visit_managed_roots(visit));
118        if !complete {
119            return false;
120        }
121        if let Some(continuation) = &self.continuation
122            && !continuation.visit_managed_roots(visit)
123        {
124            return false;
125        }
126        self.roots.visit_managed_roots(visit) && self.handlers.visit_managed_roots(visit)
127    }
128}
129
130/// Failure to admit another explicit frame.
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132pub enum FrameStackError {
133    /// A push would exceed the caller-declared frame budget.
134    DepthExhausted {
135        /// Frames present before the refused push.
136        depth: usize,
137        /// Maximum admitted frame depth.
138        limit: usize,
139    },
140}
141
142/// An explicit activation stack whose depth never consumes the host call stack.
143#[derive(Clone, Debug, PartialEq, Eq)]
144pub struct FrameStack<F> {
145    frames: Vec<F>,
146    limit: WorkLimit,
147}
148
149impl<F> FrameStack<F> {
150    /// Creates an empty frame stack using the control organ's work-limit vocabulary.
151    pub fn new(limit: WorkLimit) -> Self {
152        Self {
153            frames: Vec::new(),
154            limit,
155        }
156    }
157
158    /// Returns the occupied frame depth.
159    pub fn depth(&self) -> usize {
160        self.frames.len()
161    }
162
163    /// Admits one frame or returns typed depth exhaustion without recursion.
164    pub fn push(&mut self, frame: F) -> Result<(), FrameStackError> {
165        if self.frames.len() >= self.limit.0 {
166            return Err(FrameStackError::DepthExhausted {
167                depth: self.frames.len(),
168                limit: self.limit.0,
169            });
170        }
171        self.frames.push(frame);
172        Ok(())
173    }
174
175    /// Removes the current frame, if any.
176    pub fn pop(&mut self) -> Option<F> {
177        self.frames.pop()
178    }
179
180    /// Returns the current frame, if any.
181    pub fn current(&self) -> Option<&F> {
182        self.frames.last()
183    }
184
185    /// Returns the mutable current frame, if any.
186    pub fn current_mut(&mut self) -> Option<&mut F> {
187        self.frames.last_mut()
188    }
189
190    /// Visits frames in deterministic caller-to-current order.
191    pub fn visit_frames(&self, mut visit: impl FnMut(&F)) {
192        for frame in &self.frames {
193            visit(frame);
194        }
195    }
196}
197
198impl<F: ManagedRootSource> ManagedRootSource for FrameStack<F> {
199    fn visit_managed_roots(
200        &self,
201        visit: &mut dyn FnMut(sim_lib_mutation::ManagedId) -> bool,
202    ) -> bool {
203        for frame in &self.frames {
204            if !frame.visit_managed_roots(visit) {
205                return false;
206            }
207        }
208        true
209    }
210}
211
212/// Malformed value-width evidence in a transfer packet.
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
214pub enum TransferError {
215    /// Value and width sequences have different lengths.
216    WidthCountMismatch,
217    /// Logical widths must be nonzero.
218    ZeroWidth,
219}
220
221fn validate_widths<V>(values: &[V], widths: &[usize]) -> Result<(), TransferError> {
222    if values.len() != widths.len() {
223        return Err(TransferError::WidthCountMismatch);
224    }
225    if widths.contains(&0) {
226        return Err(TransferError::ZeroWidth);
227    }
228    Ok(())
229}
230
231/// Guest-neutral call data: a code reference and width-accounted values.
232#[derive(Clone, Debug, PartialEq, Eq)]
233pub struct CallTransfer<V, C> {
234    /// Values entering the target activation.
235    pub values: Vec<V>,
236    /// Logical storage width corresponding one-for-one with `values`.
237    pub widths: Vec<usize>,
238    /// Consumer-defined reference to prepared code.
239    pub target: C,
240}
241
242impl<V, C> CallTransfer<V, C> {
243    /// Validates and constructs an explicit call transfer.
244    pub fn new(values: Vec<V>, widths: Vec<usize>, target: C) -> Result<Self, TransferError> {
245        validate_widths(&values, &widths)?;
246        Ok(Self {
247            values,
248            widths,
249            target,
250        })
251    }
252}
253
254/// Guest-neutral return data carrying width-accounted values to a continuation.
255#[derive(Clone, Debug, PartialEq, Eq)]
256pub struct ReturnTransfer<V> {
257    /// Values leaving the current activation.
258    pub values: Vec<V>,
259    /// Logical storage width corresponding one-for-one with `values`.
260    pub widths: Vec<usize>,
261}
262
263impl<V> ReturnTransfer<V> {
264    /// Validates and constructs an explicit return transfer.
265    pub fn new(values: Vec<V>, widths: Vec<usize>) -> Result<Self, TransferError> {
266        validate_widths(&values, &widths)?;
267        Ok(Self { values, widths })
268    }
269}
270
271/// Explicit control transfer interpreted by a consumer-owned machine driver.
272#[derive(Clone, Debug, PartialEq, Eq)]
273pub enum Transfer<V, C> {
274    /// Enter prepared code with explicit values and widths.
275    Call(CallTransfer<V, C>),
276    /// Resume the saved continuation with explicit values and widths.
277    Return(ReturnTransfer<V>),
278}