1#![allow(unused)]
2
3use std::{
4 env::current_dir,
5 path::{Path, PathBuf},
6 str::FromStr,
7};
8
9use anyhow::{Context, Error};
10use parking_lot::Mutex;
11use rustc_hash::FxHashMap;
12use swc_common::sync::{Lazy, OnceCell};
13
14use crate::{
15 plugin_module_bytes::{CompiledPluginModuleBytes, PluginModuleBytes, RawPluginModuleBytes},
16 runtime,
17};
18
19#[derive(Default)]
20pub struct PluginModuleCacheInner {
21 current_runtime: Option<&'static str>,
22
23 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
24 fs_cache_root: Option<String>,
25 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
26 fs_cache_store: Option<FileSystemCache>,
27 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
31 fs_cache_hash_store: FxHashMap<String, String>,
32 memory_cache_store: FxHashMap<String, Vec<u8>>,
34 compiled_module_bytes: FxHashMap<String, runtime::ModuleCache>,
38}
39
40impl PluginModuleCacheInner {
41 pub fn get_fs_cache_root(&self) -> Option<String> {
42 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
43 return self.fs_cache_root.clone();
44
45 None
46 }
47
48 pub fn contains(&self, rt: &dyn runtime::Runtime, key: &str) -> bool {
50 if Some(rt.identifier()) != self.current_runtime {
51 return false;
52 }
53
54 let is_in_cache = self.memory_cache_store.contains_key(key)
55 || self.compiled_module_bytes.contains_key(key);
56
57 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
58 {
59 return is_in_cache || self.fs_cache_hash_store.contains_key(key);
63 }
64
65 is_in_cache
66 }
67
68 pub fn insert_raw_bytes(&mut self, key: String, value: Vec<u8>) {
75 self.memory_cache_store.insert(key, value);
76 }
77
78 fn insert_compiled_module_bytes(&mut self, key: String, value: runtime::ModuleCache) {
89 self.compiled_module_bytes.insert(key, value);
90 }
91
92 pub fn store_bytes_from_path(
94 &mut self,
95 rt: &dyn runtime::Runtime,
96 binary_path: &Path,
97 key: &str,
98 ) -> Result<(), Error> {
99 let runtime_name = rt.identifier();
100
101 if *self.current_runtime.get_or_insert(runtime_name) != runtime_name {
104 self.current_runtime = Some(runtime_name);
105 self.compiled_module_bytes.clear();
106 }
107
108 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
109 {
110 let raw_module_bytes =
111 std::fs::read(binary_path).context("Cannot read plugin from specified path")?;
112
113 if let Some(fs_cache_store) = &mut self.fs_cache_store {
115 let module_bytes_hash = blake3::hash(&raw_module_bytes).to_string();
116
117 let module =
118 if let Some(cache) = unsafe { fs_cache_store.load(rt, &module_bytes_hash) } {
119 tracing::debug!("Build WASM from cache: {key}");
120 cache
121 } else {
122 let cache = rt
123 .prepare_module(&raw_module_bytes)
124 .context("Cannot compile plugin binary")?;
125 fs_cache_store.store(rt, &module_bytes_hash, &cache)?;
126 cache
127 };
128
129 self.fs_cache_hash_store
131 .insert(key.to_string(), module_bytes_hash);
132
133 self.insert_compiled_module_bytes(key.to_string(), module);
135 }
136
137 self.insert_raw_bytes(key.to_string(), raw_module_bytes);
139
140 return Ok(());
141 }
142
143 anyhow::bail!("Filesystem cache is not enabled, cannot read plugin from phsyical path");
144 }
145
146 pub fn get(&self, rt: &dyn runtime::Runtime, key: &str) -> Option<Box<dyn PluginModuleBytes>> {
150 if let Some(compiled_module) = self.compiled_module_bytes.get(key) {
153 let cache = rt.clone_cache(compiled_module)?;
154 return Some(Box::new(CompiledPluginModuleBytes::new(
155 key.to_string(),
156 cache,
157 )));
158 }
159
160 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
162 if let Some(fs_cache_store) = &self.fs_cache_store {
163 let hash = self.fs_cache_hash_store.get(key)?;
164 let module = unsafe { fs_cache_store.load(rt, hash) };
165 if let Some(module) = module {
166 return Some(Box::new(CompiledPluginModuleBytes::new(
167 key.to_string(),
168 module,
169 )));
170 }
171 }
172
173 if let Some(memory_cache_bytes) = self.memory_cache_store.get(key) {
176 return Some(Box::new(RawPluginModuleBytes::new(
177 key.to_string(),
178 memory_cache_bytes.clone(),
179 )));
180 }
181 None
182 }
183}
184
185#[derive(Default)]
186pub struct PluginModuleCache {
187 pub inner: OnceCell<Mutex<PluginModuleCacheInner>>,
188 instantiation_lock: Mutex<()>,
192}
193
194impl PluginModuleCache {
195 pub fn create_inner(
196 enable_fs_cache_store: bool,
197 fs_cache_store_root: Option<&str>,
198 ) -> PluginModuleCacheInner {
199 PluginModuleCacheInner {
200 current_runtime: None,
201
202 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
203 fs_cache_root: fs_cache_store_root.map(|s| s.to_string()),
204 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
205 fs_cache_store: if enable_fs_cache_store {
206 FileSystemCache::create(fs_cache_store_root)
207 } else {
208 None
209 },
210 #[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
211 fs_cache_hash_store: Default::default(),
212 memory_cache_store: Default::default(),
213 compiled_module_bytes: Default::default(),
214 }
215 }
216}
217
218#[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
219struct FileSystemCache {
220 path: PathBuf,
221}
222
223#[cfg(all(not(target_arch = "wasm32"), feature = "filesystem_cache"))]
224impl FileSystemCache {
225 #[tracing::instrument(level = "info", skip_all)]
226 fn create(root: Option<&str>) -> Option<Self> {
227 let mut root_path = if let Some(root) = root {
228 Some(PathBuf::from(root))
229 } else if let Ok(cwd) = current_dir() {
230 Some(cwd.join(".swc"))
231 } else {
232 None
233 };
234
235 let mut path = root_path?;
236 path.push("plugins");
237 path.push(format!(
238 "{}_{}_{}",
239 std::env::consts::OS,
240 std::env::consts::ARCH,
241 option_env!("CARGO_PKG_VERSION").unwrap_or("plugin_runner_unknown")
242 ));
243
244 std::fs::create_dir_all(&path).ok()?;
245 Some(FileSystemCache { path })
246 }
247
248 unsafe fn load(&self, rt: &dyn runtime::Runtime, key: &str) -> Option<runtime::ModuleCache> {
249 let path = self.path.join(format!("{}.{}", key, rt.identifier()));
250 rt.load_cache(&path)
251 }
252
253 fn store(
254 &self,
255 rt: &dyn runtime::Runtime,
256 key: &str,
257 cache: &runtime::ModuleCache,
258 ) -> anyhow::Result<()> {
259 let path = self.path.join(format!("{}.{}", key, rt.identifier()));
260 rt.store_cache(&path, cache)
261 }
262}