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}