use super::ExtClsInfo;
use crate::{
compiler::EvalStage,
interner::{ToSymbol, TypeNodeId},
interpreter::Value,
plugin::MacroInfo,
runtime::{
Time,
vm::{Machine, ReturnCode},
},
};
use std::{any::Any, cell::RefCell, rc::Rc};
pub type SystemPluginFnType<T> = fn(&mut T, &mut Machine) -> ReturnCode;
pub type SystemPluginMacroType<T> = fn(&mut T, &[(Value, TypeNodeId)]) -> Value;
pub struct SysPluginSignature {
name: &'static str,
fun: Rc<dyn Any>,
ty: TypeNodeId,
stage: EvalStage,
}
impl SysPluginSignature {
pub fn new<F, T>(name: &'static str, fun: F, ty: TypeNodeId) -> Self
where
F: Fn(&mut T, &mut Machine) -> ReturnCode + 'static,
T: SystemPlugin,
{
Self {
name,
fun: Rc::new(fun),
ty,
stage: EvalStage::Stage(1),
}
}
pub fn new_macro<F, T>(name: &'static str, fun: F, ty: TypeNodeId) -> Self
where
F: Fn(&mut T, &[(Value, TypeNodeId)]) -> Value + 'static,
T: SystemPlugin,
{
Self {
name,
fun: Rc::new(fun),
ty,
stage: EvalStage::Stage(0),
}
}
pub fn get_name(&self) -> &'static str {
self.name
}
pub fn get_type(&self) -> TypeNodeId {
self.ty
}
pub fn get_stage(&self) -> EvalStage {
self.stage
}
}
pub trait SystemPlugin {
fn as_any_mut(&mut self) -> &mut dyn Any;
fn generate_audioworker(&mut self) -> Option<Box<dyn SystemPluginAudioWorker>> {
None
}
fn on_init(&mut self, _machine: &mut Machine) -> ReturnCode {
0
}
fn after_main(&mut self, _machine: &mut Machine) -> ReturnCode {
0
}
#[cfg(not(target_arch = "wasm32"))]
fn on_init_wasm(
&mut self,
_engine: &mut crate::runtime::wasm::engine::WasmEngine,
) -> ReturnCode {
0
}
#[cfg(not(target_arch = "wasm32"))]
fn after_main_wasm(
&mut self,
_engine: &mut crate::runtime::wasm::engine::WasmEngine,
) -> ReturnCode {
0
}
fn gen_interfaces(&self) -> Vec<SysPluginSignature>;
fn try_get_main_loop(&mut self) -> Option<Box<dyn FnOnce()>> {
None
}
fn freeze_audio_handle(&mut self) -> Option<Box<dyn Any + Send>> {
None
}
#[cfg(not(target_arch = "wasm32"))]
fn freeze_for_wasm(&mut self) -> Option<crate::runtime::wasm::WasmPluginFnMap> {
None
}
#[cfg(not(target_arch = "wasm32"))]
fn generate_wasm_audioworker(
&mut self,
) -> Option<Box<dyn crate::runtime::wasm::WasmSystemPluginAudioWorker>> {
None
}
}
pub trait SystemPluginAudioWorker {
fn on_sample(&mut self, _time: Time, _machine: &mut Machine) -> ReturnCode {
0
}
fn gen_interfaces(&self) -> Vec<SysPluginSignature>;
}
pub struct DynSystemPlugin {
inner: Rc<RefCell<dyn SystemPlugin>>,
audioworker: Option<Box<dyn SystemPluginAudioWorker>>,
pub clsinfos: Vec<ExtClsInfo>,
pub macroinfos: Vec<MacroInfo>,
}
impl DynSystemPlugin {
pub fn take_audioworker(&mut self) -> Option<Box<dyn SystemPluginAudioWorker>> {
self.audioworker.take()
}
pub fn freeze_audio_handle(&mut self) -> Option<Box<dyn Any + Send>> {
self.inner.borrow_mut().freeze_audio_handle()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn freeze_for_wasm(&mut self) -> Option<crate::runtime::wasm::WasmPluginFnMap> {
self.inner.borrow_mut().freeze_for_wasm()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn generate_wasm_audioworker(
&mut self,
) -> Option<Box<dyn crate::runtime::wasm::WasmSystemPluginAudioWorker>> {
self.inner.borrow_mut().generate_wasm_audioworker()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn on_init_wasm(
&self,
engine: &mut crate::runtime::wasm::engine::WasmEngine,
) -> crate::runtime::vm::ReturnCode {
self.inner.borrow_mut().on_init_wasm(engine)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn after_main_wasm(
&self,
engine: &mut crate::runtime::wasm::engine::WasmEngine,
) -> crate::runtime::vm::ReturnCode {
self.inner.borrow_mut().after_main_wasm(engine)
}
pub fn borrow_inner_mut(&self) -> std::cell::RefMut<'_, dyn SystemPlugin> {
self.inner.borrow_mut()
}
}
impl<T> From<T> for DynSystemPlugin
where
T: SystemPlugin + Sized + 'static,
{
fn from(mut plugin: T) -> Self {
let mut audioworker = plugin.generate_audioworker();
let ifs = plugin.gen_interfaces();
let inner: Rc<RefCell<dyn SystemPlugin>> = Rc::new(RefCell::new(plugin));
let macroinfos = ifs
.iter()
.filter(|&SysPluginSignature { stage, .. }| matches!(stage, EvalStage::Stage(0)))
.map(|SysPluginSignature { name, fun, ty, .. }| {
let inner = inner.clone();
let fun = fun
.clone()
.downcast::<SystemPluginMacroType<T>>()
.expect("invalid conversion applied in the system plugin resolution.");
MacroInfo::new(
name.to_symbol(),
*ty,
Rc::new(RefCell::new(move |args: &[(Value, TypeNodeId)]| -> Value {
let mut plugin_ref = inner.borrow_mut();
let p: &mut T = plugin_ref
.as_any_mut()
.downcast_mut::<T>()
.expect("plugin type mismatch");
fun(p, args)
})),
)
})
.collect();
let clsinfos = ifs
.into_iter()
.chain(
audioworker
.as_mut()
.map(|worker| worker.gen_interfaces())
.into_iter()
.flatten(),
)
.filter(|SysPluginSignature { stage, .. }| matches!(stage, EvalStage::Stage(1)))
.map(|SysPluginSignature { name, fun, ty, .. }| {
let inner = inner.clone();
let fun = fun
.clone()
.downcast::<SystemPluginFnType<T>>()
.expect("invalid conversion applied in the system plugin resolution.");
let fun = Rc::new(RefCell::new(move |machine: &mut Machine| -> ReturnCode {
let mut plugin_ref = inner.borrow_mut();
let p: &mut T = plugin_ref
.as_any_mut()
.downcast_mut::<T>()
.expect("plugin type mismatch");
fun(p, machine)
}));
ExtClsInfo::new(name.to_symbol(), ty, fun)
})
.collect();
DynSystemPlugin {
inner,
audioworker,
clsinfos,
macroinfos,
}
}
}