refactor(plugin/runner): Reuse wasmer

This commit is contained in:
Donny 2022-01-05 14:01:57 +09:00
parent c3895ca9aa
commit 28ff0592a4
5 changed files with 389 additions and 671 deletions

952
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -21,10 +21,8 @@ swc_common = {version = "0.16.0", path = "../swc_common", features = ["plugin-rt
swc_ecma_ast = {version = "0.62.0", path = "../swc_ecma_ast"}
swc_ecma_parser = {version = "0.84.0", path = "../swc_ecma_parser"}
swc_plugin_js_api = {version = "0.8.0", path = "../swc_plugin_js_api"}
wasmtime = "0.32.1"
wasmtime-wasi = "0.32.1"
wasmer = "2.1.1"
wasmer-cache = "2.1.1"
[dev-dependencies]
testing = {version = "0.17.0", path = "../testing"}
[features]

View File

@ -9,10 +9,8 @@ use parking_lot::Mutex;
use resolve::PluginCache;
use swc_common::collections::AHashMap;
use swc_ecma_ast::Program;
// we have few targets wasmer does not supports yet
// https://github.com/wasmerio/wasmer/issues/2324
use wasmtime::{Engine, Linker, Store};
use wasmtime_wasi::WasiCtxBuilder;
use wasmer::{imports, Instance, Module, Store};
use wasmer_cache::{Cache, Hash};
pub mod resolve;
@ -29,7 +27,7 @@ pub mod resolve;
/// [This code](https://github.com/swc-project/swc/blob/fc4c6708f24cda39640fbbfe56123f2f6eeb2474/crates/swc/src/plugin.rs#L19-L44)
/// includes previous incorrect attempt to workaround file read issues.
/// In actual transform, `plugins` is also being called per each transform.
fn load_plugin(plugin_path: &Path, _cache: &mut Option<PluginCache>) -> Result<(), Error> {
fn load_plugin(plugin_path: &Path, cache: &mut Option<PluginCache>) -> Result<Instance, Error> {
static BYTE_CACHE: Lazy<Mutex<AHashMap<PathBuf, Arc<Vec<u8>>>>> =
Lazy::new(|| Default::default());
@ -38,33 +36,72 @@ fn load_plugin(plugin_path: &Path, _cache: &mut Option<PluginCache>) -> Result<(
// process won't be reflected.
// 2. If reading binary fails somehow it won't bail out but keep retry.
let module_bytes_key = plugin_path.to_path_buf();
let _module_bytes =
if let Some(cached_bytes) = BYTE_CACHE.lock().get(&module_bytes_key).cloned() {
cached_bytes
} else {
let fresh_module_bytes = std::fs::read(plugin_path)
.map(Arc::new)
.context("Cannot read plugin from specified path")?;
BYTE_CACHE
.lock()
.insert(module_bytes_key, fresh_module_bytes.clone());
let module_bytes = if let Some(cached_bytes) = BYTE_CACHE.lock().get(&module_bytes_key).cloned()
{
cached_bytes
} else {
let fresh_module_bytes = std::fs::read(plugin_path)
.map(Arc::new)
.context("Cannot read plugin from specified path")?;
BYTE_CACHE
.lock()
.insert(module_bytes_key, fresh_module_bytes.clone());
fresh_module_bytes
};
fresh_module_bytes
};
// TODO: can we share store instances across each plugin binaries?
let engine = Engine::default();
let wasmer_store = Store::default();
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let load_from_cache = |c: &mut PluginCache, hash: Hash| match c {
PluginCache::File(filesystem_cache) => unsafe {
filesystem_cache.load(&wasmer_store, hash)
},
};
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build();
let mut _store = Store::new(&engine, wasi);
let store_into_cache = |c: &mut PluginCache, hash: Hash, module: &Module| match c {
PluginCache::File(filesystem_cache) => filesystem_cache.store(hash, module),
};
Ok(())
let hash = Hash::generate(&module_bytes);
let load_cold_wasm_bytes =
|| Module::new(&wasmer_store, module_bytes.as_ref()).context("Cannot compile plugin");
let module = if let Some(cache) = cache {
let cached_module =
load_from_cache(cache, hash).context("Failed to load plugin from cache");
match cached_module {
Ok(module) => Ok(module),
Err(err) => {
let loaded_module = load_cold_wasm_bytes().map_err(|_| err);
match &loaded_module {
Ok(module) => {
if let Err(err) = store_into_cache(cache, hash, &module) {
loaded_module
.map_err(|_| err)
.context("Failed to store compiled plugin into cache")
} else {
loaded_module
}
}
Err(..) => loaded_module,
}
}
}
} else {
load_cold_wasm_bytes()
};
return match module {
Ok(module) => {
let import_object = imports! {};
Instance::new(&module, &import_object).context("Failed to create plugin instance")
}
Err(err) => Err(err.into()),
};
}
pub fn apply_js_plugin(
@ -75,7 +112,7 @@ pub fn apply_js_plugin(
program: Program,
) -> Result<Program, Error> {
(|| -> Result<_, Error> {
load_plugin(path, cache)?;
let _instance = load_plugin(path, cache)?;
// TODO: actually apply transform from plugin
Ok(program)
})()

View File

@ -9,12 +9,13 @@ use std::{
sync::Arc,
};
use swc_common::collections::AHashMap;
use wasmer_cache::FileSystemCache;
/// Type of cache to store compiled bytecodes of plugins.
/// Currently only supports filesystem, but _may_ supports
/// other type (in-memory, etcs) for the long-running processes like devserver.
pub enum PluginCache {
File,
File(FileSystemCache),
}
/// TODO: Cache
@ -59,7 +60,7 @@ pub fn resolve(name: &str) -> Result<Arc<PathBuf>, Error> {
/// Note SWC's plugin should not fail to load when cache location is not
/// available. It'll make each invocation to cold start.
pub fn resolve_plugin_cache_root(root: Option<String>) -> Result<PluginCache, Error> {
let _root_path = match root {
let root_path = match root {
Some(root) => {
let mut root = PathBuf::from(root);
root.push("plugins");
@ -73,7 +74,9 @@ pub fn resolve_plugin_cache_root(root: Option<String>) -> Result<PluginCache, Er
}
};
Ok(PluginCache::File)
FileSystemCache::new(root_path)
.map(|cache| PluginCache::File(cache))
.context("Failed to create cache location for the plugins")
}
#[derive(Deserialize)]

View File

@ -1 +1 @@
nightly-2021-12-15
nightly-2022-01-02