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}