swc_plugin_runner/
runtime.rs

1//! Plugin runtime abstract
2
3use std::{any::Any, fmt, path::Path};
4
5/// Runtime FFI Value
6pub type Value = i32;
7
8/// Opaque module cache
9pub struct ModuleCache(pub Box<dyn Any + Send + Sync>);
10
11/// Module cache or raw bytes module
12pub enum Module {
13    Cache(ModuleCache),
14    Bytes(Box<[u8]>),
15}
16
17/// Plugin function define
18pub struct Func {
19    /// Number of parameters and return values
20    pub sign: (u8, u8),
21    /// The closure
22    pub func: Box<dyn Fn(&mut dyn Caller<'_>, &[Value], &mut [Value]) + Send + Sync>,
23}
24
25/// Plugin runtime abstract
26pub trait Runtime: fmt::Debug + Send + Sync {
27    /// An identifier used to identify the runtime implement,
28    /// which should include the runtime name and version.
29    fn identifier(&self) -> &'static str;
30
31    /// Preprocess raw bytes into module cache
32    ///
33    /// Note that it is not mandatory to implement AOT. It can just be loaded as
34    /// an object.
35    fn prepare_module(&self, bytes: &[u8]) -> anyhow::Result<ModuleCache>;
36
37    /// Initialize the plugin instance
38    ///
39    /// * `name` and `envs` parameters are module name and environment variable
40    ///   pairs. They can be ignored for non-wasi modules.
41    /// * `imports` parameters should be provided as `env` module.
42    /// * The runtime should call the `__get_transform_plugin_core_pkg_diag`
43    ///   function once after instantiation to check that initialization was
44    ///   completed correctly.
45    fn init(
46        &self,
47        name: &str,
48        imports: Vec<(String, Func)>,
49        envs: Vec<(String, String)>,
50        module: Module,
51    ) -> anyhow::Result<Box<dyn Instance>>;
52
53    /// Clone module cache
54    ///
55    /// If the runtime does not allow clone, then it can return `None`.
56    fn clone_cache(&self, _cache: &ModuleCache) -> Option<ModuleCache> {
57        None
58    }
59
60    /// Load a module from file
61    ///
62    /// # Safety
63    ///
64    /// This function is marked as unsafe, allow to load trusted module cache
65    /// without verification.
66    unsafe fn load_cache(&self, _path: &Path) -> Option<ModuleCache> {
67        None
68    }
69
70    /// Store a module to file
71    fn store_cache(&self, _path: &Path, _cache: &ModuleCache) -> anyhow::Result<()> {
72        Ok(())
73    }
74}
75
76/// Instance Accessor
77pub trait Caller<'a> {
78    /// Read data from instance memory
79    fn read_buf(&self, ptr: u32, buf: &mut [u8]) -> anyhow::Result<()>;
80
81    /// Write data to instance memory
82    fn write_buf(&mut self, ptr: u32, buf: &[u8]) -> anyhow::Result<()>;
83
84    /// Allocate memory in instance
85    fn alloc(&mut self, size: u32) -> anyhow::Result<u32>;
86
87    /// Free memory in instance
88    fn free(&mut self, ptr: u32, size: u32) -> anyhow::Result<u32>;
89}
90
91/// Plugin instance
92pub trait Instance: Send + Sync {
93    /// Execute transform.
94    ///
95    /// The program parameter should use [Caller::alloc] to allocate
96    /// and write `PluginSerializedBytes` data.
97    fn transform(
98        &mut self,
99        program_ptr: u32,
100        program_len: u32,
101        unresolved_mark: u32,
102        should_enable_comments_proxy: u32,
103    ) -> anyhow::Result<u32>;
104
105    /// Get instance accessor
106    fn caller(&mut self) -> anyhow::Result<Box<dyn Caller<'_> + '_>>;
107
108    /// Export Module cache
109    fn cache(&self) -> Option<ModuleCache>;
110
111    /// Perform cleanup operations
112    fn cleanup(&mut self) -> anyhow::Result<()> {
113        Ok(())
114    }
115}
116
117impl Func {
118    /// Create a plugin function from closure
119    pub fn from_fn<const A: usize, const R: usize, F>(f: F) -> Self
120    where
121        F: Fn(&mut dyn Caller<'_>, [i32; A]) -> [i32; R] + Send + Sync + 'static,
122    {
123        Func {
124            sign: (A.try_into().unwrap(), R.try_into().unwrap()),
125            func: Box::new(move |store, args, rv| {
126                let args = args.try_into().unwrap();
127                rv.copy_from_slice(&f(store, args));
128            }),
129        }
130    }
131}