Skip to main content

mimium_lang/
runtime.rs

1use crate::{
2    compiler::IoChannelInfo,
3    utils::{error::ReportableError, metadata::Location},
4};
5use thiserror::Error;
6
7pub mod ffi;
8pub mod ffi_serde;
9pub mod primitives;
10pub mod vm;
11pub mod vm_ffi;
12
13#[cfg(not(target_arch = "wasm32"))]
14pub mod wasm;
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16pub struct Time(pub u64);
17
18/// Return code from DSP execution (alias for vm::ReturnCode).
19pub type ReturnCode = i64;
20
21/// Type-safe representation of programs that can be hot-swapped into a runtime.
22///
23/// This replaces the previous `Box<dyn Any + Send>` approach with an explicit
24/// enum, providing compile-time type safety and eliminating runtime downcasts.
25pub enum ProgramPayload {
26    /// Native bytecode VM program.
27    VmProgram(vm::Program),
28    /// WASM module bytes with optional DSP state skeleton for state migration.
29    #[cfg(not(target_arch = "wasm32"))]
30    WasmModule {
31        bytes: Vec<u8>,
32        /// Preloaded WASM execution engine prepared on non-RT side.
33        /// RT side swaps this engine in without loading/instantiating the module.
34        prepared_engine: Box<crate::runtime::wasm::engine::WasmEngine>,
35        /// State tree skeleton of the DSP function in the new program.
36        /// When present, enables state-preserving hot-swap via
37        /// `state_tree::update_state_storage`.
38        dsp_state_skeleton: Option<state_tree::tree::StateTreeSkeleton<crate::mir::StateType>>,
39        /// Precomputed patch plan for migrating old state storage into
40        /// the new state layout.
41        state_patch_plan: state_tree::StateStoragePatchPlan,
42        /// Global state snapshot taken after running `main` on a
43        /// prewarmed runtime outside the audio thread.
44        prewarmed_global_state: Vec<u64>,
45    },
46}
47
48/// Abstraction over per-sample DSP execution backends.
49///
50/// This trait decouples audio drivers from a concrete runtime implementation,
51/// allowing both the native bytecode VM and the WASM backend to be used
52/// interchangeably with the same `Driver` infrastructure (cpal, CSV, etc.).
53pub trait DspRuntime {
54    /// Execute the DSP function for one sample tick.
55    fn run_dsp(&mut self, time: Time) -> ReturnCode;
56
57    /// Read the output produced by the last `run_dsp` call.
58    ///
59    /// The returned slice should contain at least `n_channels` elements.
60    fn get_output(&self, n_channels: usize) -> &[f64];
61
62    /// Write input samples that will be available during the next `run_dsp` call.
63    fn set_input(&mut self, input: &[f64]);
64
65    /// I/O channel configuration (None if the dsp function was not found).
66    fn io_channels(&self) -> Option<IoChannelInfo>;
67
68    /// Update sample rate for runtimes that keep an internal runtime state.
69    fn set_sample_rate(&mut self, _sample_rate: f64) {}
70
71    /// Attempt to hot-swap the running program.
72    ///
73    /// Takes a type-safe `ProgramPayload` which can be either a native VM
74    /// program or WASM module bytes. Returns `true` on success.
75    fn try_hot_swap(&mut self, new_program: ProgramPayload) -> bool {
76        let _ = new_program;
77        false
78    }
79
80    /// Upcast to `Any` so callers can downcast to a concrete runtime type.
81    fn as_any(&self) -> &dyn std::any::Any;
82    /// Mutable upcast to `Any`.
83    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
84}
85
86#[derive(Debug, Error)]
87pub enum ErrorKind {
88    #[error("Unknown Error")]
89    Unknown,
90}
91
92#[derive(Debug, Error)]
93#[error("Runtime Error: {0}")]
94pub struct RuntimeError(pub ErrorKind, pub Location);
95
96impl ReportableError for RuntimeError {
97    fn get_labels(&self) -> Vec<(crate::utils::metadata::Location, String)> {
98        vec![(self.1.clone(), self.0.to_string())]
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    /// Compile-time assertion that `ProgramPayload` implements `Send`.
107    #[test]
108    fn ensure_payload_is_send() {
109        fn assert_send<T: Send>() {}
110        assert_send::<ProgramPayload>();
111    }
112}