Skip to main content

vihaco_stdlib/
loadable.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! An independently owned image paired with its mutable execution machine.
5
6use vihaco_abi::{traits::Reset, word::Word};
7use vihaco_module::module::LocalModule;
8use vihaco_runtime::{Composite, Dispatch, HasProgram};
9
10use crate::{ExecutionState, Image, execution_state};
11
12/// A program image and the composite that executes its borrowed instructions.
13///
14/// Construct the image explicitly before pairing it with a machine. Only
15/// [`Image`] is supported; the image remains unchanged throughout an invocation.
16/// Ordinary outer structs can own several loadables with different instruction
17/// sets. Section selection and image construction remain caller responsibilities.
18#[derive(Debug)]
19pub struct Loadable<I, M: Composite> {
20    image: I,
21    machine: M,
22}
23
24impl<Inst, V, Ty, Info, M: Composite> Loadable<Image<Inst, V, Ty, Info>, M> {
25    /// Inspect the paired image without changing an invocation's program data.
26    pub fn image(&self) -> &Image<Inst, V, Ty, Info> {
27        &self.image
28    }
29
30    /// Inspect the execution machine and its devices.
31    pub fn machine(&self) -> &M {
32        &self.machine
33    }
34
35    /// Configure the execution machine or its devices.
36    ///
37    /// Direct changes to execution state can affect whether an invocation can
38    /// resume. The caller is responsible for keeping those changes consistent
39    /// with the paired image.
40    pub fn machine_mut(&mut self) -> &mut M {
41        &mut self.machine
42    }
43
44    /// Consume the owner, returning its image and machine without resetting them.
45    pub fn into_parts(self) -> (Image<Inst, V, Ty, Info>, M) {
46        (self.image, self.machine)
47    }
48}
49
50impl<Inst, V, Ty, Info, M> Loadable<Image<Inst, V, Ty, Info>, M>
51where
52    M: Composite + HasProgram<Program = ExecutionState>,
53{
54    /// Pair an image with a machine, resetting its previous invocation state.
55    ///
56    /// Device state is retained. No parsing, lowering, or image validation is
57    /// performed here; construction of a valid image is the caller's job.
58    pub fn new(image: Image<Inst, V, Ty, Info>, mut machine: M) -> Self {
59        machine.program_mut().reset();
60        Self { image, machine }
61    }
62
63    /// Install an already constructed image and reset execution state.
64    ///
65    /// Device state is retained. Image construction happens before this call;
66    /// mutations made by the caller while constructing it are not rolled back.
67    ///
68    /// # Errors
69    ///
70    /// Rejects replacement while an invocation is active, including one that
71    /// could resume after an execution error. Call [`Self::reset`] to abandon
72    /// that invocation first. Rejection leaves the current image and execution
73    /// state unchanged.
74    pub fn replace_image(&mut self, image: Image<Inst, V, Ty, Info>) -> eyre::Result<()> {
75        eyre::ensure!(
76            self.machine.program().is_halted(),
77            "cannot replace an active program"
78        );
79        self.image = image;
80        self.reset();
81        Ok(())
82    }
83
84    /// Abandon the invocation, retaining the image and all device state.
85    pub fn reset(&mut self) {
86        self.machine.program_mut().reset();
87    }
88}
89
90impl<Inst, V, Ty, Info, M> Loadable<Image<Inst, V, Ty, Info>, M>
91where
92    M: HasProgram<Program = ExecutionState>
93        + Dispatch<LocalModule<Inst, V, Ty, Info>, Instruction = Inst>,
94{
95    /// Start at the program entrypoint with the supplied arguments.
96    ///
97    /// Instructions and metadata borrow the image while dispatch mutates the
98    /// separate machine. No instruction cloning is required.
99    ///
100    /// # Errors
101    ///
102    /// Returns the runner's entry or execution errors. Earlier machine mutations
103    /// are retained; reset or resume according to the invocation's state.
104    pub fn execute(&mut self, arguments: &[Word]) -> eyre::Result<()> {
105        execution_state::execute(&self.image.module, &mut self.machine, arguments)
106    }
107
108    /// Continue the existing invocation against the same paired image.
109    ///
110    /// # Errors
111    ///
112    /// Returns the runner's state, instruction-fetch, or dispatch errors without
113    /// rolling back machine mutations. A halted invocation remains halted.
114    pub fn resume(&mut self) -> eyre::Result<()> {
115        execution_state::resume(&self.image.module, &mut self.machine)
116    }
117}