mimium_lang/plugin/
system_plugin.rs

1//! Defines the plugin interface used by mimium's runtime.
2//!
3//! A system plugin can hook into the VM execution by providing callback
4//! functions.  Each plugin exposes its callbacks through [`SysPluginSignature`]
5//! values that are registered as external closures.
6
7use super::ExtClsInfo;
8use crate::{
9    compiler::EvalStage,
10    interner::{ToSymbol, TypeNodeId},
11    interpreter::Value,
12    plugin::MacroInfo,
13    runtime::{
14        Time,
15        vm::{Machine, ReturnCode},
16    },
17};
18use std::{
19    any::Any,
20    cell::{RefCell, UnsafeCell},
21    rc::Rc,
22    sync::Arc,
23};
24pub type SystemPluginFnType<T> = fn(&mut T, &mut Machine) -> ReturnCode;
25pub type SystemPluginMacroType<T> = fn(&mut T, &[(Value, TypeNodeId)]) -> Value;
26
27/// Metadata for a callback provided by a [`SystemPlugin`].
28///
29/// Each signature stores the callback name, erased function pointer and the
30/// type of the closure expected by the VM.
31pub struct SysPluginSignature {
32    name: &'static str,
33    /// The function internally implements `Fn(&mut T:SystemPlugin,&mut Machine)->ReturnCode`
34    /// but the type is erased for dynamic dispatching. later the function is downcasted into their own type.
35    fun: Rc<dyn Any>,
36    ty: TypeNodeId,
37    /// The stage at which the function is available.
38    /// This is used to determine whether the function can be called in a macro or
39    /// in the VM. Note that any persistent functions are not allowed to be used in `SystemPlugin`.
40    stage: EvalStage,
41}
42impl SysPluginSignature {
43    pub fn new<F, T>(name: &'static str, fun: F, ty: TypeNodeId) -> Self
44    where
45        F: Fn(&mut T, &mut Machine) -> ReturnCode + 'static,
46        T: SystemPlugin,
47    {
48        Self {
49            name,
50            fun: Rc::new(fun),
51            ty,
52            stage: EvalStage::Stage(1),
53        }
54    }
55    pub fn new_macro<F, T>(name: &'static str, fun: F, ty: TypeNodeId) -> Self
56    where
57        F: Fn(&mut T, &[(Value, TypeNodeId)]) -> Value + 'static,
58        T: SystemPlugin,
59    {
60        Self {
61            name,
62            fun: Rc::new(fun),
63            ty,
64            stage: EvalStage::Stage(0),
65        }
66    }
67}
68
69/// Trait implemented by runtime plugins.
70///
71/// The default implementations of the callback methods do nothing. Plugins can
72/// override these to perform setup in [`on_init`], teardown in [`after_main`],
73/// or per-sample processing in [`on_sample`].
74pub trait SystemPlugin {
75    fn on_init(&mut self, _machine: &mut Machine) -> ReturnCode {
76        0
77    }
78    fn after_main(&mut self, _machine: &mut Machine) -> ReturnCode {
79        0
80    }
81    fn on_sample(&mut self, _time: Time, _machine: &mut Machine) -> ReturnCode {
82        0
83    }
84    fn gen_interfaces(&self) -> Vec<SysPluginSignature>;
85    fn try_get_main_loop(&mut self) -> Option<Box<dyn FnOnce()>> {
86        None
87    }
88}
89
90#[derive(Clone)]
91/// A dynamically dispatched plugin wrapped in reference-counted storage.
92pub struct DynSystemPlugin {
93    pub inner: Arc<UnsafeCell<dyn SystemPlugin>>,
94    pub clsinfos: Vec<ExtClsInfo>,
95    pub macroinfos: Vec<MacroInfo>,
96}
97/// Convert a plugin into the VM-facing representation.
98///
99/// The returned [`DynSystemPlugin`] is stored by the runtime, while the
100/// accompanying `Vec<ExtClsInfo>` contains closures that expose the plugin's
101/// callback methods to mimium code.
102impl<T> From<T> for DynSystemPlugin
103where
104    T: SystemPlugin + Sized + 'static,
105{
106    fn from(plugin: T) -> Self {
107        let ifs = plugin.gen_interfaces();
108        let inner = Arc::new(UnsafeCell::new(plugin));
109        let macroinfos = ifs
110            .iter()
111            .filter(|&SysPluginSignature { stage, .. }| matches!(stage, EvalStage::Stage(0)))
112            .map(|SysPluginSignature { name, fun, ty, .. }| {
113                let inner = inner.clone();
114                let fun = fun
115                    .clone()
116                    .downcast::<SystemPluginMacroType<T>>()
117                    .expect("invalid conversion applied in the system plugin resolution.");
118                MacroInfo::new(
119                    name.to_symbol(),
120                    *ty,
121                    Rc::new(RefCell::new(move |args: &[(Value, TypeNodeId)]| -> Value {
122                        // breaking double borrow rule at here!!!
123                        // Also here I do dirty downcasting because here the type of plugin is ensured as T.
124                        unsafe {
125                            let p = inner.get().as_mut().unwrap();
126                            fun(p, args)
127                        }
128                    })),
129                )
130            })
131            .collect();
132        let clsinfos = ifs
133            .into_iter()
134            .filter(|SysPluginSignature { stage, .. }| matches!(stage, EvalStage::Stage(1)))
135            .map(|SysPluginSignature { name, fun, ty, .. }| {
136                let inner = inner.clone();
137                let fun = fun
138                    .clone()
139                    .downcast::<SystemPluginFnType<T>>()
140                    .expect("invalid conversion applied in the system plugin resolution.");
141                let fun = Rc::new(RefCell::new(move |machine: &mut Machine| -> ReturnCode {
142                    // breaking double borrow rule at here!!!
143                    // Also here I do dirty downcasting because here the type of plugin is ensured as T.
144                    unsafe {
145                        let p = inner.get().as_mut().unwrap();
146                        fun(p, machine)
147                    }
148                }));
149                ExtClsInfo::new(name.to_symbol(), ty, fun)
150            })
151            .collect();
152
153        DynSystemPlugin {
154            inner,
155            clsinfos,
156            macroinfos,
157        }
158    }
159}